Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite

This commit is contained in:
2026-04-15 10:54:23 +08:00
51 changed files with 5678 additions and 3244 deletions

View File

@@ -116,8 +116,18 @@
() => props.isEdit,
(v) => (v ? edit() : cancel())
)
const edit = () => {}
const cancel = () => {}
const edit = () => {
formData.storeName = "测试"
formData.fullName = "测试"
formData.email = "测试"
formData.phoneNumber = "测试"
formData.description = "测试"
formData.links = ["https://www.baidu.com", "https://www.taobao.com"]
}
const cancel = () => {
formRef.value.clearValidate()
edit()
}
const submit = async () => {
const valid = await formRef.value.validate()
if (!valid) return Promise.reject(false)

View File

@@ -0,0 +1,166 @@
<template>
<a-modal
class="image-clip-dialog generalModel"
v-model:visible="show"
:footer="null"
width="70%"
:maskClosable="false"
:centered="true"
:closable="false"
wrapClassName="#app"
:keyboard="false"
>
<div class="image-clip-dialog-box">
<div class="header">
<div class="title">{{ data.title }}</div>
<div class="right">
<div class="submit" v-if="!data.isPreview" @click="onSubmit">
<svg-icon name="seller-dui" size="24" />
</div>
<button @click="onCancel">Cancel</button>
</div>
</div>
<div class="content">
<image-clip
ref="imageClipRef"
:ratio="data.ratio"
:url="data.url"
@change="(v) => (data.preview_url = v)"
/>
<div class="preview" v-if="data.isPreview">
<div class="title">
<span class="icon"><svg-icon name="seller-preview" size="24" /></span>
<span class="label">Crop Preview</span>
</div>
<img :src="data.preview_url" />
<div class="submit" @click="onSubmit">
<svg-icon name="seller-dui" size="24" />
</div>
</div>
</div>
</div>
</a-modal>
</template>
<script setup>
import { ref } from "vue"
import ImageClip from "./image-clip.vue"
const data = reactive({
url: "",
title: "Crop Image",
preview_url: "",
ratio: [1, 1],
isPreview: true,
callback: null
})
const show = ref(false)
const open = (url, callback, options) => {
if (!url || !callback) return
data.url = url
data.callback = callback
data.ratio = [1, 1]
data.isPreview = true
data.preview_url = ""
data.title = "Crop Image"
if (options) {
if (options.hasOwnProperty("isPreview")) data.isPreview = options.isPreview
if (options.hasOwnProperty("ratio")) data.ratio = options.ratio
if (options.hasOwnProperty("title")) data.title = options.title
}
show.value = true
}
const onCancel = () => {
show.value = false
}
const imageClipRef = ref(null)
const onSubmit = () => {
imageClipRef.value.getCropBlob().then((blob) => {
if (data.callback) data.callback(blobToFile(blob, "image.png"))
onCancel()
})
}
// 将blob转换为file对象
const blobToFile = (blob, fileName) => {
return new File([blob], fileName, { type: blob.type })
}
defineExpose({
open
})
</script>
<style scoped lang="less">
.image-clip-dialog-box {
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
.submit {
width: 4rem;
height: 4rem;
border-radius: 50%;
background: #262626;
color: #fff;
cursor: pointer;
}
> .header {
display: flex;
justify-content: space-between;
margin-bottom: 5rem;
> .title {
font-family: pingfang_heavy;
font-size: 2.4rem;
color: #595959;
}
> .right {
display: flex;
align-items: center;
justify-content: center;
gap: 2rem;
> button {
width: 10rem;
height: 4.8rem;
border-radius: 4rem;
border: none;
background: #e4e5eb;
font-family: pingfang_heavy;
font-size: 1.6rem;
color: #000;
}
}
}
> .content {
flex: 1;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
> .image-clip {
flex: 1;
}
> .preview {
margin-left: 6rem;
width: 28rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 2.4rem;
> .title {
display: flex;
align-items: center;
justify-content: center;
gap: 1.2rem;
> .label {
font-family: pingfang_heavy;
font-size: 1.6rem;
}
}
> img {
width: 100%;
height: auto;
margin-bottom: 3rem;
}
}
}
}
</style>

View File

@@ -0,0 +1,157 @@
<template>
<div class="image-clip">
<div class="image-clip-body" ref="imageClipBody">
<VueCropper
ref="cropper"
:img="url"
crossOrigin="Anonymous"
:autoCrop="true"
:fixedNumber="ratio"
fixed
movable
centerBox
@realTime="onChange"
></VueCropper>
</div>
<div class="clip_opterate">
<div class="item" @click="rotateLeft()">
<span class="icon iconfont icon-chexiao operate_icon"></span>
</div>
<div class="item" @click="rotateRight()">
<span class="icon iconfont icon-chexiao operate_icon icon_chexiao_sec"></span>
</div>
<div class="item" @click="changeScale(-0.1)">
<span class="operate_icon icon_font">-</span>
</div>
<div class="item" @click="changeScale(0.1)">
<span class="operate_icon icon_font">+</span>
</div>
<div class="item" @click="refreshCrop()">
<span class="icon iconfont icon-shuaxin operate_icon"></span>
</div>
</div>
</div>
</template>
<script setup>
import { ref, useAttrs, onMounted, onBeforeUnmount } from "vue"
import "vue-cropper/dist/index.css"
import { VueCropper } from "vue-cropper"
const props = defineProps({
url: {
type: String,
default: ""
},
ratio: {
type: Array,
default: () => [1, 1]
}
})
const attrs = useAttrs()
const onChange = (data) => {
if (attrs.onChange) {
getCropUrl().then((url) => attrs.onChange(url))
}
}
const cropper = ref(null)
const imageClipBody = ref(null)
const observer = new ResizeObserver((entries) => {
refreshCrop()
})
onMounted(() => {
observer.observe(imageClipBody.value)
})
onBeforeUnmount(() => {
observer.disconnect()
})
const rotateLeft = () => {
cropper.value.rotateLeft()
}
const rotateRight = () => {
cropper.value.rotateRight()
}
const refreshCrop = () => {
cropper.value.refresh()
}
const changeScale = (num = 1) => {
cropper.value.changeScale(num)
}
const getCropUrl = () => {
return new Promise((resolve, reject) => {
cropper.value.getCropData(resolve)
})
}
const getCropBlob = () => {
return new Promise((resolve, reject) => {
cropper.value.getCropBlob(resolve)
})
}
defineExpose({
getCropUrl,
getCropBlob
})
</script>
<style lang="less" scoped>
.image-clip {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
// height: 100%;
background: #fff;
border-radius: calc(2rem * 1.2);
padding: calc(1.3rem * 1.2) calc(1.3rem * 1.2) calc(2rem * 1.2);
box-sizing: border-box;
.image-clip-body {
width: 100%;
height: calc(40rem * 1.2);
// height: 53rem;
background: yellow;
}
.clip_opterate {
margin: calc(2.7rem * 1.2) auto 0;
border-radius: calc(1.6rem * 1.2);
display: flex;
overflow: hidden;
border: 1px solid #e2e2e4;
width: calc(24rem * 1.2);
.item {
width: calc(4.7rem * 1.2);
height: calc(4rem * 1.2);
display: flex;
align-items: center;
justify-content: center;
border-right: 0.1rem solid #e6e8ea;
cursor: pointer;
.icon_chexiao_sec {
transform: rotateY(180deg); /* 垂直镜像翻转 */
}
.operate_icon {
font-size: calc(1.8rem * 1.2);
color: rgba(102, 102, 102, 1);
font-weight: bold;
}
.icon_font {
font-size: calc(2.5rem * 1.2);
position: relative;
top: calc(-0.3rem * 1.2);
user-select: none;
}
.icon-shuaxin {
font-size: calc(1.4rem * 1.2);
}
&:last-child {
border: none;
}
}
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div class="brand-profile-index">
<div class="brand-profile-index mini-scrollbar">
<div class="header">
<div class="bg">
<img v-if="banner" :src="banner" />
@@ -7,7 +7,7 @@
<span class="icon"><svg-icon name="seller-picture" size="60" /></span>
<span class="tip">Your brand banner has not been set up yet.</span>
</div>
<button>Change Brand Banner</button>
<button @click="onChangeBanner">Change Brand Banner</button>
</div>
<!-- 头像 -->
<div class="avatar">
@@ -15,7 +15,7 @@
<div v-else class="null">
<svg-icon name="seller-user" size="48" />
</div>
<span class="icon">
<span class="icon" @click="onChangeAvatar">
<svg-icon name="seller-camera" size="24" />
</span>
</div>
@@ -37,15 +37,56 @@
<p class="tip">&nbsp;</p>
</template>
</div>
<image-clip-dialog ref="imageClipDialogRef" />
</template>
<script setup>
import { ref } from "vue"
import BrandInfo from "./brand-info.vue"
const banner = ref("http://118.31.39.42:3000/falls/5bd8065cbb396eb5a8ef0a142605139358734e57.png")
const avatar = ref("http://118.31.39.42:3000/falls/20251024140128_10355_1.jpg")
import ImageClipDialog from "./image-clip-dialog.vue"
const banner = ref("")
const avatar = ref("")
const isEdit = ref(false)
const brandInfoRef = ref(null)
const imageClipDialogRef = ref(null)
// 选择本机图片
const uploadImg = (onChange) => {
const input = document.createElement("input")
input.type = "file"
input.accept = "image/*"
// 监听文件输入框的变化事件
input.addEventListener("change", (event) => {
event.preventDefault()
const file = event.target.files[0]
const url = URL.createObjectURL(file)
onChange({ url, file })
})
input.click()
}
const onChangeBanner = () => {
uploadImg(({ url }) => {
imageClipDialogRef.value.open(
url,
(file) => {
banner.value = URL.createObjectURL(file)
},
{ ratio: [40, 7], isPreview: false, title: "Crop Brand Banner" }
)
})
}
const onChangeAvatar = () => {
uploadImg(({ url }) => {
imageClipDialogRef.value.open(
url,
(file) => {
avatar.value = URL.createObjectURL(file)
},
{ ratio: [1, 1], isPreview: true, title: "Crop Avatar" }
)
})
}
const onEdit = () => {
isEdit.value = true
}

View File

@@ -0,0 +1,227 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
//const props = defineProps({
//})
const emit = defineEmits([
'selectCollectionItem',
])
const current = ref(1)
const list = ref([
{
imgList:[
{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},
],
type:'Series',
name:'Christmas',
sketchNum: 7,
date: 'today',
id:'1',
},{
imgList:[
{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},
],
type:'Series',
name:'Christmas',
sketchNum: 7,
date: 'today',
id:'2',
},{
imgList:[
{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},
],
type:'Series',
name:'Christmas',
sketchNum: 7,
date: 'today',
id:'1',
},{
imgList:[
{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},
],
type:'Series',
name:'Christmas',
sketchNum: 7,
date: 'today',
id:'1',
},
])
const selectCollectionItem = (item:any)=>{
emit('selectCollectionItem',item)
}
onMounted(()=>{
})
onUnmounted(()=>{
})
defineExpose({})
</script>
<template>
<div class="historyList">
<div class="list">
<div v-for="(item,index) in list" :key="index" class="item" @click="selectCollectionItem(item)">
<div class="imgList">
<div v-for="(img,index) in item.imgList" :key="index" class="img">
<img :src="img.url" alt="">
</div>
</div>
<div class="detail">
<div class="name">{{item.name}}</div>
<div class="bottom">
<div>{{item.sketchNum}} sketchs</div>
<div>{{item.date}}</div>
</div>
</div>
<div class="type" :class="item.type">
{{item.type}}
</div>
</div>
</div>
<div class="pagination">
<a-pagination v-model:current="current" :pageSize="6" :showSizeChanger="false" show-quick-jumper :total="100" show-less-items />
</div>
</div>
</template>
<style lang="less" scoped>
.historyList{
width: 100%;
height: 100%;
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
position: relative;
justify-content: space-between;
align-items: center;
margin-top: 3rem;
> .list{
height: 100%;
overflow: hidden;
display: grid;
grid-template-columns: repeat(3, 1fr);
row-gap: 2.4rem; /* 垂直间距 3px */
column-gap: 3.2rem; /* 横向间距 2px */
align-content: start;
overflow-y: auto;
&::-webkit-scrollbar{
display: none;
}
> .item{
width: 42.6rem;
height: auto;
// height: 27.6rem;
border: 1.5px solid #C7C7C7;
position: relative;
overflow: hidden;
border-radius: 2rem;
cursor: pointer;
transition: all .3s;
> .imgList{
background-color: #eaeaea;
height: 19.2rem;
padding: 1rem 1.3rem;
display: flex;
gap: .4rem;
> .img{
width: 9.7rem;
height: 17.2rem;
border-radius: 1rem;
overflow: hidden;
> img{
object-fit: cover;
height: 100%;
width: 100%;
}
}
}
> .detail{
padding: 2rem 2.4rem;
> .name{
font-family: pingfang_heavy;
font-weight: 400;
font-size: 1.797rem;
line-height: 1;
}
> .bottom{
display: flex;
margin-top: .8rem;
justify-content: space-between;
> div{
font-size: 1.2rem;
font-family: pingfang_regular;
font-weight: 400;
font-size: 1.6rem;
line-height: 1;
color: #585858;
}
> div:nth-child(2){
color: #979797;
}
}
}
> .type{
width: 7.6rem;
height: 3rem;
border-radius: 2rem;
border: 1px solid #727070;
background: #7E7C7C;
font-weight: 500;
font-size: 1.4rem;
line-height: 150%;
color: #fff;
position: absolute;
top: .5rem;
right: .5rem;
display: flex;
justify-content: center;
align-items: center;
&.Series{
}
&.Single{
background-color: #ffffff;
color: #727070;
}
}
&:hover{
border: 1.5px solid #000;
}
}
}
> .pagination{
margin-top: 3rem;
}
}
</style>

View File

@@ -1,13 +1,25 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import sellerHeader from "../../seller-header.vue"
import historyList from "./historyList.vue"
import { useRouter } from "vue-router"
//const props = defineProps({
//})
//const emit = defineEmits([
//])
const router = useRouter()
let data = reactive({
})
const searchType = ref('all')
const searchText = ref('')
const historyListRef = ref(null)
const handleSearch = () => {
}
const selectCollectionItem = (item:any) => {
router.push({path:'/home/seller/myListings/select/'+item.id})
}
onMounted(()=>{
})
onUnmounted(()=>{
@@ -26,7 +38,27 @@ const {} = toRefs(data);
>
</seller-header>
<div class="content">
1231222aaa
<div class="title">
<div class="left">
<div :class="{active:searchType == 'all'}" @click="searchType = 'all'">All</div>
<div :class="{active:searchType == 'series'}" @click="searchType = 'series'">Series Design</div>
<div :class="{active:searchType == 'single'}" @click="searchType = 'single'">Single Design</div>
</div>
<div class="right">
<div class="search_input flex flex-align-center">
<input
class="search_input_inner"
v-model="searchText"
:bordered="false"
@keydown.enter="handleSearch"
placeholder="123123"
/>
<!-- <SearchOutlined class="search_input_icon" @click="handleSearch" /> -->
<SvgIcon name="CSearch" size="20" class="search_input_icon" @click="handleSearch" />
</div>
</div>
</div>
<historyList ref="historyListRef" @selectCollectionItem="selectCollectionItem"></historyList>
</div>
</div>
</template>
@@ -42,6 +74,71 @@ const {} = toRefs(data);
margin-top: 2rem;
flex: 1;
overflow: hidden;
padding: 2.4rem 3rem;
background-color: #f9fafa;
border-radius: 2rem;
display: flex;
flex-direction: column;
> .title{
display: flex;
justify-content: space-between;
> .left{
display: flex;
align-items: center;
gap: 1.2rem;
> div{
min-width: 9rem;
padding: .4rem 2.6rem;
background-color: #fff;
border-radius: 4rem;
border: 1px solid #000000;
font-weight: 500;
font-size: 1.6rem;
line-height: 150%;
text-align: center;
cursor: pointer;
&:hover{
color: #fff;
background-color: #000;
}
}
> .active{
color: #fff;
background-color: #000;
}
}
> .right{
.search_input {
height: 3.2rem;
width: 27rem; // 默认宽度
background-color: #fff;
border: 1px solid #000;
border-radius: 3rem;
// column-gap: 3rem;
padding: 0 2rem;
.search_input_inner {
border: none;
height: 100%;
width: calc(100% - 2rem);
padding: .6rem 0;
font-size: 1.2rem;
&::placeholder {
font-size: 1.2rem;
}
&::-webkit-input-placeholder{
font-size: 1.2rem;
}
}
.search_input_icon {
font-size: 1.6rem;
color: #000;
width: initial;
height: initial;
}
}
}
}
}
}
</style>

View File

@@ -1,6 +1,8 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import sellerHeader from "../../seller-header.vue"
import { VueDraggable } from "vue-draggable-plus"
import selectMenu from '@/component/modules/selectMenu.vue'
//const props = defineProps({
//})
@@ -8,7 +10,101 @@ import sellerHeader from "../../seller-header.vue"
//])
let data = reactive({
})
const domSize = ref('Small')
const domSizeList = ref([
{
label:'Small',
value:'Small',
},
{
label:'Medium',
value:'Medium',
},
{
label:'Large',
value:'Large',
},
])
const list = ref([
{ id: "1" },
{ id: "2" },
{ id: "3" },
{ id: "4" },
{ id: "5" },
{ id: "6" },
{ id: "7" },
{ id: "8" },
{ id: "9" },
{ id: "10" }
])
const chooseList = ref([])
const chooseItem = (item:any)=>{
chooseList.value.push(item)
}
const next = ()=>{
if(chooseList.value.length == 0)return
}
const config = ref({
"data-container-type": "root",
"data-parent-id": "null",
animation: 250,
handle: ".item", // 可拖动的元素
"ghost-class": "ghost", // 拖动时的类名
"chosen-class": "chosen", // 选中时的类名
"drag-class": "drag", // 拖动时的类名
"swap-threshold": 0.5,
"empty-insert-threshold": 5,
"force-fallback": false,
"fallback-tolerance": 3,
"scroll-sensitivity": 100,
"scroll-speed": 10,
onEnd: (e) => {}
})
let resizeObserver = null as any
const gap = ref({
Small: '16px',
Medium: '24px',
Large: '30px',
})
//根据宽度设置列表宽度
let upDataDomWidthTime = null as any
const listingsBoxRef = ref(null) as any
const setDomSize = (width: number)=>{
if(!listingsBoxRef.value)return
let listDom = listingsBoxRef.value.querySelector('.list')
let listItemDom = listDom.querySelector('.item')
let offsetWidth = listItemDom.getBoundingClientRect().width
let lineNum = Math.floor(width / offsetWidth)
let itemNum = Math.floor((width - (lineNum - 1) * parseInt(gap.value[domSize.value])) / offsetWidth)
listDom.style.maxWidth = ((itemNum - 1) * parseInt(gap.value[domSize.value]) + itemNum * (offsetWidth)) + 'px'
}
const changeDomSize = ()=>{
setTimeout(()=>{
setDomSize(listingsBoxRef.value.clientWidth)
},350)
}
onMounted(()=>{
// 创建观察器
nextTick(()=>{
resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
// 获取元素尺寸
const width = entry.contentRect.width
if(upDataDomWidthTime)clearTimeout(upDataDomWidthTime)
upDataDomWidthTime = setTimeout(()=>{
setDomSize(width)
},200)
}
})
// 开始监听
if(resizeObserver)resizeObserver.observe(listingsBoxRef.value)
})
})
onUnmounted(()=>{
})
@@ -25,9 +121,95 @@ const {} = toRefs(data);
{title:'Select Sketch', name: 'myListingsSelectItem' }
]"
>
<template #right>
<div class="header-right">
<div class="chooseNum">
{{ chooseList.length }} sketches selected
</div>
<div class="button" @click="next">
<span>Next</span>
<div class="icon">
<i class="fi fi-rr-arrow-small-right"></i>
</div>
</div>
</div>
</template>
</seller-header>
<div class="content">
1231222aaa
<div class="content" ref="listingsBoxRef">
<div class="title">
<div class="left">
<i class="fi fi-rs-comments"></i>
<span>Active Listings</span>
</div>
<div class="right">
<div class="generalModel_state">
<div>
<selectMenu
:selectList="domSizeList"
@change="changeDomSize"
:isBtnOpen='true'
:style="{
'border-radius':'0rem',
'border':'none',
'font-weight': '900',
'border-right':'2px solid rgba(0,0,0,.2)',
'line-height': '3rem',
'height': '3rem',
'background': 'rgba(0,0,0,0)',
}"
v-model:select="domSize"
>
<template v-slot:btnText>
{{ $t('Header.Filter') }}
</template>
</selectMenu>
</div>
<div>
<selectMenu
:selectList="domSizeList"
@change="changeDomSize"
:isBtnOpen='true'
:style="{
'border-radius':'0rem',
'border':'none',
'font-weight': '900',
'line-height': '3rem',
'height': '3rem',
'background': 'rgba(0,0,0,0)',
}"
v-model:select="domSize"
>
<template v-slot:btnText>
{{ $t('Header.Size') }}
</template>
</selectMenu>
</div>
</div>
</div>
</div>
<VueDraggable
v-model="list"
class="list"
:class="domSize"
v-bind="config"
:style="{gap: gap[domSize] || '1.6rem'}"
:group="{
name: 'sortable',
pull: false,
put: true
}"
>
<div v-for="v in list" :key="v.id"
:draging="true"
class="item"
@click="chooseItem(v)"
:class="{'active':chooseList.some(item => item.id === v.id)}"
>
<div class="choose">
<i class="fi fi-rr-check"></i>
</div>
</div>
</VueDraggable>
</div>
</div>
</template>
@@ -39,10 +221,143 @@ const {} = toRefs(data);
position: relative;
display: flex;
flex-direction: column;
.header-right{
display: flex;
.chooseNum{
font-family: pingfang_regular;
font-weight: 400;
font-size: 1.6rem;
line-height: 140%;
margin-right: 2rem;
line-height: 6rem;
}
.button {
width: 12.3rem;
height: 6rem;
border-radius: 4rem;
display: flex;
border: 1.5px solid #000000;
background-color: #000;
color: #fff;
transition: all .3s;
gap: .8rem;
align-items: center;
justify-content: center;
font-size: 1.6rem;
cursor: pointer;
i{
color: #fff;
display: flex;
font-size: 1.6rem;
}
&:hover{
background-color: #fff;
color: #000;
i{
color: #000;
}
}
}
}
> .content {
margin-top: 2rem;
flex: 1;
overflow: hidden;
padding: 2.4rem 3rem;
background-color: #f9fafa;
border-radius: 2rem;
display: flex;
flex-direction: column;
> .title{
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 3rem;
> .left{
display: flex;
justify-content: space-between;
align-items: center;
font-family: pingfang_heavy;
font-weight: 400;
font-size: 1.8rem;
line-height: 130%;
letter-spacing: 0%;
gap: 1.2rem;
> i{
display: flex;
justify-content: space-between;
align-items: center;
font-size: 2.4rem;
}
}
}
.list{
display: flex;
flex: 1;
flex-wrap: wrap;
margin: 0 auto;
overflow-y: auto;
align-content: flex-start;
&::-webkit-scrollbar {
display: none;
}
&.Small{
--itemWidth: 17rem;
--itemHeight: 26.1rem;
--iconFS: 1.4rem;
--iconWH: 2.4rem;
--iconTR: 1.2rem;
}
&.Medium{
--itemWidth: 22.6rem;
--itemHeight: 34.6rem;
--iconFS: 1.6rem;
--iconWH: 3rem;
--iconTR: 1.4rem;
}
&.Large{
--itemWidth: 29.2rem;
--itemHeight: 44.8rem;
--iconFS: 2.2rem;
--iconWH: 4rem;
--iconTR: 1.62rem;
}
> .item{
width: var(--itemWidth);
height: var(--itemHeight);
border-radius: 2rem;
border: 1.5px solid #C7C7C7;
// transition: all .3s;
position: relative;
> .choose{
display: flex;
width: var(--iconWH);
height: var(--iconWH);
position: absolute;
background-color: #000;
border-radius: 50%;
display: none;
justify-content: center;
align-items: center;
top: var(--iconTR);
right: var(--iconTR);
> i{
color: #fff;
font-size: var(--iconFS);
display: flex;
}
}
&.active{
border: 1.5px solid #000;
> .choose{
display: flex;
}
}
&:hover{
border: 1.5px solid #000;
}
}
}
}
}
</style>

View File

@@ -1,6 +1,9 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import { VueDraggable } from "vue-draggable-plus"
import contentItem from "./contentItem.vue"
import selectMenu from '@/component/modules/selectMenu.vue'
import deleteDrafts from './deleteDrafts.vue'
//const props = defineProps({
//})
@@ -19,11 +22,6 @@ const list = ref([
{ id: "7" },
{ id: "8" },
{ id: "9" },
{ id: "9" },
{ id: "9" },
{ id: "9" },
{ id: "9" },
{ id: "9" },
{ id: "10" }
])
const list2 = ref([
@@ -54,9 +52,99 @@ const config = ref({
"scroll-speed": 10,
onEnd: (e) => {}
})
const domSize = ref('Small')
const domSizeList = ref([
{
label:'Small',
value:'Small',
},
{
label:'Medium',
value:'Medium',
},
{
label:'Large',
value:'Large',
},
])
const visible = ref(false)
const deleteDraftsRef = ref(null)
const deleteDraft = (item: any)=>{
deleteDraftsRef.value.open(()=>{
list2.value = list2.value.filter((v: any)=>v.id != item.id)
})
}
const draftListing = (item: any)=>{
//数组前面添加item
list2.value.unshift(item)
list.value = list.value.filter((v: any)=>v.id != item.id)
}
const publishListing = (item: any)=>{
list.value.unshift(item)
list2.value = list2.value.filter((v: any)=>v.id != item.id)
}
const editListing = (item: any)=>{
}
const listingsBoxRef = ref(null) as any
let resizeObserver = null as any
const gap = ref({
Small: '16px',
Medium: '24px',
Large: '30px',
})
//根据宽度设置列表宽度
let upDataDomWidthTime = null
const setDomSize = (width: number)=>{
if(!listingsBoxRef.value)return
let listDom = listingsBoxRef.value.querySelector('.list')
let listItemDom = listDom.querySelector('.item')
let offsetWidth = listItemDom.getBoundingClientRect().width
let lineNum = Math.floor(width / offsetWidth)
let itemNum = Math.floor((width - (lineNum - 1) * parseInt(gap.value[domSize.value])) / offsetWidth)
listDom.style.maxWidth = ((itemNum - 1) * parseInt(gap.value[domSize.value]) + itemNum * (offsetWidth)) + 'px'
}
const changeDomSize = ()=>{
setTimeout(()=>{
setDomSize(listingsBoxRef.value.clientWidth)
},350)
}
const unfoldDrafits = ()=>{
data.showDrafts = !data.showDrafts
}
onMounted(()=>{
// 创建观察器
nextTick(()=>{
resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
// 获取元素尺寸
const width = entry.contentRect.width
const height = entry.contentRect.height
const borderBoxSize = entry.borderBoxSize[0] // 包含边框
const contentBoxSize = entry.contentBoxSize[0] // 内容区域
if(upDataDomWidthTime)clearTimeout(upDataDomWidthTime)
upDataDomWidthTime = setTimeout(()=>{
setDomSize(width)
},200)
}
})
// 开始监听
if(resizeObserver)resizeObserver.observe(listingsBoxRef.value)
})
})
onUnmounted(()=>{
// 停止监听
if(listingsBoxRef?.value)resizeObserver?.unobserve(listingsBoxRef?.value)
})
defineExpose({})
const { showDrafts } = toRefs(data);
@@ -64,19 +152,62 @@ const { showDrafts } = toRefs(data);
<template>
<div class="listings">
<div class="listingsBox listingsBox1">
<div class="box">
<div class="box" :class="domSize" ref="listingsBoxRef">
<div class="title">
<div class="left">
<i class="fi fi-rs-comments"></i>
<span>Active Listings</span>
</div>
<div class="right">
<div class="generalModel_state">
<div>
<selectMenu
:selectList="domSizeList"
@change="changeDomSize"
:isBtnOpen='true'
:style="{
'border-radius':'0rem',
'border':'none',
'font-weight': '900',
'border-right':'2px solid rgba(0,0,0,.2)',
'line-height': '3rem',
'height': '3rem',
'background': 'rgba(0,0,0,0)',
}"
v-model:select="domSize"
>
<template v-slot:btnText>
{{ $t('Header.Filter') }}
</template>
</selectMenu>
</div>
<div>
<selectMenu
:selectList="domSizeList"
@change="changeDomSize"
:isBtnOpen='true'
:style="{
'border-radius':'0rem',
'border':'none',
'font-weight': '900',
'line-height': '3rem',
'height': '3rem',
'background': 'rgba(0,0,0,0)',
}"
v-model:select="domSize"
>
<template v-slot:btnText>
{{ $t('Header.Size') }}
</template>
</selectMenu>
</div>
</div>
</div>
</div>
<VueDraggable
v-model="list"
class="list"
:style="{gap: gap[domSize] || '1.6rem'}"
v-bind="config"
:group="{
name: 'sortable',
@@ -84,20 +215,33 @@ const { showDrafts } = toRefs(data);
put: true
}"
>
<div class="item" v-for="v in list" :key="v.id" :draging="true">
{{ v.id }}
</div>
<contentItem
v-for="v in list"
:key="v.id"
:item="v"
type="listings"
:domSize="domSize"
@draftListing="draftListing"
@editListing="editListing"
/>
</VueDraggable>
</div>
<div class="openOrCloseDrafts" :class="{'active': showDrafts}" @click="showDrafts = !showDrafts">
<div class="openOrCloseDrafts" :class="{'active': showDrafts}" @click="unfoldDrafits">
<span class="icon iconfont icon-xiala"></span>
</div>
</div>
<!-- <div class="listingsBox listingsBox2" :class="{'active': showDrafts}">
<div class="box">
<div class="listingsBox listingsBox2" :class="{'active': showDrafts}">
<div class="box Small">
<div class="title">
<div class="left">
<i class="fi fi-rs-comments"></i>
<span>Drafts</span>
</div>
</div>
<VueDraggable
v-model="list2"
class="list"
class="list2"
v-bind="config"
:group="{
name: 'sortable',
@@ -105,12 +249,20 @@ const { showDrafts } = toRefs(data);
put: true
}"
>
<div class="item" v-for="v in list" :key="v.id" :draging="true">
{{ v.id }}
</div>
<contentItem
v-for="v in list2"
:key="v.id"
:item="v"
domSize="Small"
type="drafts"
@deleteDraft="deleteDraft"
@editListing="editListing"
@publishListing="publishListing"
/>
</VueDraggable>
</div>
</div> -->
</div>
<deleteDrafts ref="deleteDraftsRef" v-model:visible="visible"></deleteDrafts>
</div>
</template>
<style lang="less" scoped>
@@ -123,16 +275,19 @@ const { showDrafts } = toRefs(data);
.listingsBox{
background-color: #f9fafa;
border-radius: 2rem;
flex: 1;
position: relative;
// overflow: hidden;
display: flex;
transition: all .3s;
.box{
width: 100%;
padding: 2.4rem 4rem;
padding: 2.4rem 0;
padding-bottom: 0;
flex-direction: column;
overflow: hidden;
display: flex;
flex: 1;
padding: 2.4rem 4rem;
> .title{
display: flex;
justify-content: space-between;
@@ -148,6 +303,7 @@ const { showDrafts } = toRefs(data);
line-height: 130%;
letter-spacing: 0%;
gap: 1.2rem;
align-content: flex-start;
> i{
display: flex;
justify-content: space-between;
@@ -156,34 +312,32 @@ const { showDrafts } = toRefs(data);
}
}
}
.list{
.list2{
gap: 1.6rem;
width: 100%;
.item{
width: 19.2rem;
}
}
.list,.list2{
display: flex;
flex-wrap: wrap;
gap: 20px;
.item{
width: 197px;
height: 249px;
border-radius: 20px;
border: 1.5px solid #C7C7C7;
&:hover{
border: 2px solid #000000;
}
// flex: 1;
margin: 0 auto;
align-items: flex-start;
overflow: auto;
&::-webkit-scrollbar {
display: none;
}
}
}
}
.listingsBox1{
width: 100%;
flex: 1;
.box{
.list{
justify-content: center;
}
}
}
.listingsBox2{
width: 48.8rem;
transition: all .3s;
overflow: hidden;
&.active{
width: 0;

View File

@@ -0,0 +1,225 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
const props = defineProps({
domSize: {
type: String,
default: 'Small'
},
type: {
type: String,
default: 'listings'
},
item: {
type: Object,
default: () => ({})
}
})
const emit = defineEmits([
'deleteDraft',
'editListing',
'draftListing',
'publishListing'
])
let data = reactive({
})
onMounted(()=>{
})
onUnmounted(()=>{
})
defineExpose({})
const {} = toRefs(data);
</script>
<template>
<div class="item" :draging="true" :class="domSize">
<div class="imgBox">
<img src="" alt="">
<div class="maskBtn">
<div @click="$emit('editListing',item)">
<svgIcon name="seller-edit" :size="domSize == 'Small'?32:domSize == 'Medium'?40:48" />
<div>Edit</div>
</div>
<div v-if="type == 'listings'" @click="$emit('draftListing',item)">
<svgIcon name="seller-draft" :size="domSize == 'Small'?32:domSize == 'Medium'?40:48" />
<div>Draft</div>
</div>
<div v-else-if="type == 'drafts'" @click="$emit('publishListing',item)">
<svgIcon name="seller-share" :size="domSize == 'Small'?32:domSize == 'Medium'?40:48" />
<div>Publish</div>
</div>
</div>
</div>
<div class="detail">
<div class="left">
<div class="name">item name</div>
<div class="price">$1123</div>
</div>
<div class="right">
<div class="detailItem" v-if="type == 'listings'">
<div class="shopping1">
<i class="fi fi-rr-shopping-bag-add"></i>
</div>
<span>123</span>
</div>
<div class="detailItem" v-if="type == 'listings'">
<div class="eye1">
<i class="fi fi-rs-eye"></i>
</div>
<span>123</span>
</div>
<div class="detailItem drafts" v-if="type == 'drafts'" @click="$emit('deleteDraft',item)">
<div class="">
<i class="fi fi-rr-trash"></i>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="less" scoped>
.item{
// height: 30.2rem;
&.Small{
--itemWidth: 19.7rem;
--itemImgHeight: 24.9rem;
--maskBtnGap: .8rem;
--maskBtnWidth: 6rem;
--maskBtnFS: 1.6rem;
--detailPadding: .8rem 1.2rem;
--detailLeftNameSize: 1.4rem;
--detailLeftPriceSize: 1.2rem;
--detailRightGap: .8rem;
--detailRightItemGap: .4rem;
--detailRightItemIcon: 1.6rem;
--detailRightItemDraftsIcon: 2rem;
}
&.Medium{
--itemWidth: 25.4rem;
--itemImgHeight: 32.2rem;
--maskBtnGap: 1rem;
--maskBtnWidth: 7.6rem;
--maskBtnFS: 2.07rem;
--detailPadding: 1.2rem 1.4rem;
--detailLeftNameSize: 1.6rem;
--detailLeftPriceSize: 1.4rem;
--detailRightGap: 1rem;
--detailRightItemGap: .4rem;
--detailRightItemIcon: 2rem;
--detailRightItemDraftsIcon: 2rem;
}
&.Large{
--itemWidth: 31.4rem;
--itemImgHeight: 39.8rem;
--maskBtnGap: 1.2rem;
--maskBtnWidth: 7.6rem;
--maskBtnFS: 2.56rem;
--detailPadding: 1.2rem 1.6rem;
--detailLeftNameSize: 1.98rem;
--detailLeftPriceSize: 1.73rem;
--detailRightGap: 1.2rem;
--detailRightItemGap: .4rem;
--detailRightItemIcon: 2.6rem;
--detailRightItemDraftsIcon: 2rem;
}
width: var(--itemWidth);
border-radius: 2rem;
border: 1.5px solid #C7C7C7;
overflow: hidden;
.imgBox{
position: relative;
height: var(--itemImgHeight);
width: 100%;
> .maskBtn{
position: absolute;
width: 100%;
height: 100%;
background: #00000066;
opacity: 0;
transition: all .3s;
top: 0;
display: flex;
justify-content: center;
align-items: center;
gap: var(--maskBtnGap);
> div{
font-family: pingfang_bold;
font-weight: 400;
font-size: var(--maskBtnFS);
line-height: 150%;
color: #fff;
cursor: pointer;
width: var(--maskBtnWidth);
text-align: center;
> div{
margin-top: .8rem;
}
}
}
}
.detail{
padding: var(--detailPadding);
display: flex;
justify-content: space-between;
--rightColor: #979797;
.left{
gap: var(--detailRightItemGap);
.name{
font-weight: 500;
font-size: var(--detailLeftNameSize);
line-height: 150%;
}
.price{
font-family: pingfang_regular;
font-weight: 400;
font-size: var(--detailLeftPriceSize);
line-height: 150%;
}
}
.right{
display: flex;
gap: var(--detailRightGap);
.detailItem{
display: flex;
gap: var(--detailRightItemGap);
flex-direction: column;
justify-content: space-between;
> div{
color: var(--rightColor);
display: flex;
justify-content: center;
> i{
font-size: var(--detailRightItemIcon);
display: flex;
}
}
> span{
color: var(--rightColor);
font-family: pingfang_regular;
font-weight: 400;
font-size: var(--detailLeftPriceSize);
line-height: 150%;
}
&.drafts{
justify-content: center;
cursor: pointer;
> div{
> i{
font-size: var(--detailRightItemDraftsIcon);
}
}
}
}
}
}
&:hover{
border: 1.5px solid #000000;
.imgBox{
> .maskBtn{
opacity: 1;
}
}
.detail{
--rightColor: #000000;
}
}
}
</style>

View File

@@ -0,0 +1,219 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
const props = defineProps({
visible: {
type: Boolean,
default: false,
}
})
const emit = defineEmits([
'update:visible'
])
const router = useRouter()
const {t} = useI18n()
let data = reactive({
})
const fun = ref(null)
let deleteDraftsRef = ref(null)
const open = (fun)=>{
fun.value = fun
emit('update:visible', true)
}
const deleteDrafts = ()=>{
if(fun.value){
fun?.value()
}
cleardata()
}
const cleardata = ()=>{
emit('update:visible', false)
}
onMounted(()=>{
})
onUnmounted(()=>{
})
defineExpose({open})
const { showAgain } = toRefs(data);
</script>
<template>
<div ref="deleteDraftsRef" class="deleteDrafts" v-if="visible">
<a-modal
class="collection generalModel"
v-model:visible="props.visible"
:footer="null"
:get-container="() => deleteDraftsRef"
width="76.4rem"
:maskClosable="false"
:centered="true"
:closable="false"
:mask="true"
:keyboard="false"
:destroyOnClose="false"
:zIndex="1000"
>
<div class="generalModel_btn">
<div class="generalModel_closeIcon" @click.stop="cleardata()">
<SvgIcon name="seller-sellerToolTipClose" size="30"></SvgIcon>
</div>
</div>
<div class="deleteTitle">
<div class="icon">
<i class="fi fi-rr-trash"></i>
</div>
<div class="titleText">
<h1>Delete this listing?</h1>
<p>Your listing and its details will be permanently removed.</p>
</div>
</div>
<div class="deleteContent">
<div class="img">
<img src="" alt="">
</div>
<div class="detail">
<div class="name">Item Name</div>
<div class="price">HK$392.00 · Draft</div>
</div>
</div>
<div class="btnBox">
<div class="btn" @click.stop="cleardata()">Cancel</div>
<div class="btn" @click.stop="deleteDrafts()">Delete</div>
</div>
</a-modal>
</div>
</template>
<style lang="less" scoped>
.deleteDrafts{
// width: 100%;
// height: 100%;
position: absolute;
:deep(.generalModel){
height: auto;
.ant-modal-body{
padding: 6rem 5.5rem;
.generalModel_btn{
.generalModel_closeIcon{
transform: translate(0%, 0%);
top: 4rem;
right: 4rem;
}
}
}
}
.deleteTitle{
margin-bottom: 4rem;
display: flex;
> .icon{
width: 10rem;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
height: 10rem;
background-color: #fceaea;
margin-right: 3.2rem;
> i{
display: flex;
font-size: 3.7rem;
color: #DF2C2C;
}
}
> .titleText{
display: flex;
flex-direction: column;
justify-content: center;
> h1{
font-size: 2.4rem;
font-family: pingfang_heavy;
font-weight: 400;
line-height: 130%;
margin-bottom: .8rem;
}
> p{
font-weight: 400;
font-family: pingfang_regular;
font-size: 2rem;
line-height: 130%;
}
}
}
.deleteContent{
width: 100%;
background-color: #f6f6f6;
border-radius: 1.2rem;
padding: 1.2rem;
margin-bottom: 4rem;
display: flex;
> .img{
width: 9.8rem;
height: 15rem;
border-radius: 1.2rem;
margin-right: 3.6rem;
overflow: hidden;
> img{
width: 100%;
height: 100%;
object-fit: cover;
}
}
> .detail{
display: flex;
align-items: flex-start;
flex-direction: column;
justify-content: center;
> .name{
margin-bottom: .8rem;
font-family: pingfang_heavy;
font-weight: 400;
font-size: 2.4rem;
line-height: 130%;
}
> .price{
font-weight: 400;
font-family: pingfang_regular;
font-size: 2rem;
line-height: 130%;
}
}
}
.btnBox{
display: flex;
justify-content: end;
> .btn{
width: 16rem;
height: 6rem;
border-radius: 4rem;
font-weight: 400;
font-size: 2.2rem;
line-height: 130%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
&:nth-child(1){
color: #999999;
background-color: #fff;
border: 1.5px solid #C7C7C7;
}
&:nth-child(2){
margin-left: 2rem;
color: #fff;
background-color: #df2c2c;
border: 1.5px solid #df2c2c;
}
}
}
}
</style>

View File

@@ -2,6 +2,7 @@
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import sellerHeader from "../../seller-header.vue"
import sellerContent from "./content.vue"
import myEvent from "@/tool/myEvents.js"
//const props = defineProps({
//})
@@ -9,6 +10,9 @@ import sellerContent from "./content.vue"
//])
let data = reactive({
})
const newListing = ()=>{
myEvent.emit('newListing')
}
onMounted(()=>{
})
onUnmounted(()=>{
@@ -23,7 +27,7 @@ const {} = toRefs(data);
tip="Active listings and unpublished inventory."
>
<template #right>
<div class="button">
<div class="button" @click="newListing">
<span>New Listing</span>
<div class="icon">
<i class="fi fi-br-plus"></i>

View File

@@ -0,0 +1,360 @@
<template>
<div class="my-orders-index mini-scrollbar">
<div class="total-box">
<div class="total-item" v-for="v in totals" :key="v.title">
<div class="title">
<span class="icon"><svg-icon :name="v.icon" size="18" /></span>
<span class="label">{{ v.title }}</span>
</div>
<div class="value">{{ v.value }}</div>
</div>
</div>
<div class="filter-box">
<div class="left">
<div class="title">All Invoice</div>
<div class="tip">A summary of all completed transactions.</div>
</div>
<div class="right">
<div class="input">
<span class="icon"
><svg-icon name="seller-search" size="20" @click="getList(true)"
/></span>
<input
type="text"
v-model="nameOrId"
placeholder="Search by item name or order ID"
@keydown.enter.prevent="getList(true)"
/>
</div>
</div>
</div>
<div class="table">
<div class="header">
<div class="order-id">Order ID</div>
<div class="item">Item</div>
<div class="price">Price</div>
<div class="buyer-username">Buyer Username</div>
<div class="date">Date</div>
</div>
<div class="body">
<div class="item" v-for="v in list" :key="v.orderId">
<div class="order-id">{{ v.orderId }}</div>
<div class="item">
<div class="images">
<img
v-for="(v, i) in v.item.slice(0, maxItemNum)"
:key="i"
:src="v.url"
/>
<span v-if="v.item.length > maxItemNum"
>+{{ v.item.length - maxItemNum }} more</span
>
</div>
<div class="titles">
<div v-for="(v, i) in v.item.slice(0, maxItemNum)" :key="i">
{{ v.title }}
</div>
<span v-if="v.item.length > maxItemNum">...</span>
</div>
</div>
<div class="price">{{ v.price }}</div>
<div class="buyer-username">{{ v.username }}</div>
<div class="date">
<div>{{ v.date }}</div>
<div>{{ v.time }}</div>
</div>
</div>
</div>
<div class="placeholder" ref="placeholderRef" v-show="!loading"></div>
<div class="footer" v-if="!finish"><a-spin :delay="0.5" v-show="loading" /></div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from "vue"
const totals = ref([
{
icon: "seller-qiandaizi",
title: "Total Revenue",
value: "HK$ 54,32.00"
},
{
icon: "seller-gouwudai",
title: "Total Purchases",
value: "128"
},
{
icon: "seller-eye",
title: "Total Views",
value: "4,982"
}
])
const maxItemNum = ref(2)
const loading = ref(false)
const finish = ref(false)
const total = ref(0)
const page = ref(1)
const size = ref(10)
const nameOrId = ref("")
const list = ref([])
const getList = (isReload = false) => {
loading.value = true
if (isReload) {
list.value = []
page.value = 1
finish.value = false
}
const data = {
page: page.value,
size: size.value
}
if (nameOrId.value) data.nameOrId = nameOrId.value
console.log(data)
setTimeout(() => {
for (let i = 0; i < size.value; i++) {
let { date, time, dateTime } = formatTimestamp(new Date(2026, 4, 20, 13, 14).getTime())
list.value.push({
orderId: "SP" + Math.random().toString().substring(2, 10),
price: "HK$ " + (Math.random() * 500).toFixed(2),
username: "@liuyuchen",
date: date,
time: time,
item: [
{
url: "http://118.31.39.42:3000/falls/o-1.png",
title: "North Outfit Set"
},
{
url: "http://118.31.39.42:3000/falls/o-2.png",
title: "Heritage Layered Set"
},
{},
{}
]
})
}
total.value = 30
page.value++
finish.value = page.value > total.value / 10
loading.value = false
}, 1000)
}
getList(true)
const placeholderRef = ref(null)
const observer = new IntersectionObserver(
(entries) => {
if (!entries[0].intersectionRatio || loading.value || finish.value) return
getList()
},
{ root: document.body }
)
onMounted(() => {
observer.observe(placeholderRef.value)
})
onBeforeUnmount(() => {
observer.disconnect()
})
const formatTimestamp = (ts) => {
const d = new Date(ts)
const h = d.getHours()
const m = d.getMinutes()
const date = `${d.toLocaleString("en-US", {
month: "long"
})} ${d.getDate()}, ${d.getFullYear()}`
const time = `${h.toString().padStart(2, "0")}:${m.toString().padStart(2, "0")} ${
h >= 12 ? "PM" : "AM"
}`
return {
date,
time,
dateTime: `${date}\n${time}`
}
}
</script>
<style scoped lang="less">
.my-orders-index {
position: relative;
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
padding: 0 3rem;
margin: 0 3rem;
gap: 4rem;
> .total-box {
display: flex;
gap: 1.8rem;
> .total-item {
flex: 1;
padding: 2.4rem;
background-color: #f6f6f6;
border-radius: 1.2rem;
> .title {
display: flex;
align-items: center;
margin-bottom: 1.2rem;
gap: 1.2rem;
> .icon {
width: 4rem;
height: 4rem;
border-radius: 50%;
background-color: #ffffff;
border: 0.08rem solid #e4e4e4;
}
> .label {
font-size: 2rem;
color: #585858;
}
}
> .value {
font-family: "pingfang_heavy";
font-size: 3.6rem;
}
}
}
> .filter-box {
display: flex;
justify-content: space-between;
align-items: flex-end;
> .left {
> .title {
font-family: "pingfang_heavy";
font-size: 2.4rem;
color: #000;
}
> .tip {
font-family: "pingfang_regular";
font-size: 1.4rem;
color: #999;
}
}
> .right {
> .input {
width: 30rem;
height: 4rem;
border-bottom: 0.15rem solid #000000;
display: flex;
align-items: center;
> .icon {
margin: 0 0.5rem;
}
> input {
padding: 0 2rem;
width: 0;
flex: 1;
outline: none;
border: none;
height: 100%;
font-family: "pingfang_regular";
font-size: 1.2rem;
color: #000;
&::placeholder {
color: #999999;
}
}
}
}
}
> .table {
width: 100%;
> .body > .item,
> .header {
display: flex;
align-items: center;
> div {
padding: 0 1.5rem;
flex: 1;
}
> .order-id {
flex: 1.5;
}
> .item {
flex: 3;
}
> .buyer-username {
flex: 1.5;
}
}
> .header {
position: sticky;
top: 0;
background-color: #f6f6f6;
height: 5.6rem;
border-width: 0.15rem 0 0.15rem 0;
border-style: solid;
border-color: #eaeaea;
> div {
font-size: 2rem;
color: #979797;
}
}
> .body {
> .item {
padding-top: 1.6rem;
padding-bottom: 1.6rem;
border-bottom: 0.1rem solid #f6f6f6;
color: #000;
> .order-id {
font-family: "pingfang_regular";
font-size: 1.8rem;
}
> .price {
font-family: "pingfang_medium";
font-size: 1.8rem;
}
> .buyer-username {
font-family: "pingfang_regular";
font-size: 1.8rem;
color: #666;
}
> .date {
font-family: "pingfang_regular";
font-size: 1.6rem;
color: #666;
}
> .item {
display: flex;
align-items: center;
gap: 3rem;
> .images {
display: flex;
align-items: center;
justify-content: center;
gap: 1.2rem;
> img {
width: auto;
height: 10rem;
border-radius: 0.8rem;
border: 0.1rem solid #e9e9e9;
}
> span {
font-family: "pingfang_medium";
font-size: 1.4rem;
color: #666;
}
}
> .titles {
font-family: "pingfang_medium";
font-size: 1.8rem;
color: #000;
> span {
user-select: none;
}
}
}
}
}
> .footer {
min-height: 10rem;
display: flex;
align-items: center;
justify-content: center;
}
> .placeholder {
width: 100%;
height: 1px;
}
}
}
</style>

View File

@@ -0,0 +1,323 @@
<template>
<div class="settings-index">
<div>
<div class="notification">
<div class="header">Notifications</div>
<div class="content">
<div class="left">
<div class="title">New order notification</div>
<div class="tip">Receive an inbox message when a new order is placed.</div>
</div>
<div class="right">
<a-switch v-model:checked="checked" />
</div>
</div>
</div>
<div class="payout">
<div class="header">Payout</div>
<div class="content">
<div class="header">
<div class="title">Payment Providers</div>
<div class="tip">Select how you want to receive payments.</div>
</div>
<div class="pay-item" v-for="v in payList" :key="v.type">
<div class="left">
<img :src="v.icon" />
<div class="value">{{ v.value || "Unbound" }}</div>
</div>
<div class="right">
<button v-if="v.value" class="manage">Manage</button>
<button v-else class="bind-now">Bind Now</button>
</div>
</div>
<div class="footer">
<div class="left">
<div class="title">Payment Currency</div>
<div class="tip">HKD - Hong Kong Dollar</div>
</div>
<div class="right">
<button>Fixed</button>
</div>
</div>
</div>
</div>
</div>
<div>
<div class="data-privacy">
<div class="header">Data & Privacy</div>
<div class="content">
<div class="title">Copyright licence</div>
<div class="tip">
A licence certificate is automatically included with every purchase
download. View the default licensing terms applied to your listings.
</div>
<div class="tip">
This licence is issued by Code-Create and is legally binding upon purchase.
It certifies the buyer's right to use the purchased design asset in
accordance with the terms below.
</div>
<div class="tip">
For custom licensing arrangements, <span>contact us</span>.
</div>
<div class="btns">
<button>
<span class="icon"><svg-icon name="seller-download" size="14" /></span>
<span class="label">Download to View</span>
</button>
</div>
</div>
</div>
<div class="stop">
<div class="header">Stop Selling</div>
<div class="content">
<div class="title">Deactivate seller account</div>
<div class="tip">
Permanently deactivate your seller account. All listings and invoice records
will be deleted. You may re-register as a seller in the future, but your
previous sales data cannot be recovered.
</div>
<div class="btns">
<button>Deactivate</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from "vue"
import paypal from "@/assets/images/seller/setting/paypal.png"
import stripe from "@/assets/images/seller/setting/stripe.png"
import alipayHk from "@/assets/images/seller/setting/alipay-hk.png"
import alipayChinese from "@/assets/images/seller/setting/alipay-chinese.png"
const checked = ref(true)
const payList = ref([
{
type: "paypal",
icon: paypal,
value: ""
},
{
type: "stripe",
icon: stripe,
value: ""
},
{
type: "alipay-hk",
icon: alipayHk,
value: "123123"
},
{
type: "alipay-chinese",
icon: alipayChinese,
value: "123123"
}
])
</script>
<style scoped lang="less">
.settings-index {
flex: 1;
overflow-y: auto;
margin: 0 16rem;
display: flex;
gap: 4.2rem;
> div {
flex: 1;
display: flex;
flex-direction: column;
gap: 4.2rem;
> div {
border-radius: 1.2rem;
overflow: hidden;
border: 0.15rem solid #d1d1d1;
> .header {
line-height: 5rem;
background-color: #f6f6f6;
padding-left: 2.4rem;
font-size: 2rem;
color: #666;
}
> .content {
padding: 2.4rem;
}
}
> .stop {
border-color: rgba(223, 44, 44, 0.2);
> .header {
background-color: rgba(223, 44, 44, 0.2);
}
}
> .notification > .content {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.8rem;
> .left {
> .title {
font-family: pingfang_medium;
font-size: 1.6rem;
color: #000;
}
> .tip {
font-family: pingfang_regular;
font-size: 1.4rem;
color: #999;
}
}
> .right {
.ant-switch {
width: 4.8rem;
height: 2.6rem;
}
}
}
> .payout > .content {
display: flex;
flex-direction: column;
gap: 0.8rem;
> .header {
> .title {
font-family: pingfang_medium;
font-size: 1.6rem;
color: #000;
}
> .tip {
font-family: pingfang_regular;
font-size: 1.4rem;
color: #999;
}
}
> .pay-item {
padding: 1.7rem 2.4rem;
border-radius: 0.8rem;
border: 0.06rem solid #c4c4c4;
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.8rem;
> .left {
> img {
width: auto;
height: 1.8rem;
margin-right: 0.8rem;
}
> .value {
font-family: pingfang_regular;
font-size: 1.2rem;
color: #585858;
}
}
> .right {
> button {
width: 10rem;
height: 3.8rem;
border-radius: 2rem;
font-family: pingfang_heavy;
font-size: 1.2rem;
color: #fff;
background: #000000;
border: none;
cursor: pointer;
&:active {
opacity: 0.8;
}
}
> .manage {
background: #fff;
border: 0.1rem solid #000000;
color: #000000;
}
}
}
> .footer {
margin-top: 2rem;
border-top: 0.1rem solid #c4c4c4;
padding: 1.4rem 2.5rem;
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.8rem;
> .left {
> .title {
font-family: pingfang_medium;
font-size: 1.6rem;
color: #000;
}
> .tip {
font-family: pingfang_regular;
font-size: 1.2rem;
color: #999;
}
}
> .right {
> button {
width: 10rem;
height: 3.8rem;
border-radius: 2rem;
font-family: pingfang_heavy;
font-size: 1.2rem;
color: #bdbdbd;
background: #f6f6f6;
border: none;
// cursor: pointer;
// &:active {
// opacity: 0.8;
// }
}
}
}
}
> .stop > .content,
> .data-privacy > .content {
> .title {
font-family: pingfang_medium;
font-size: 1.6rem;
color: #000;
margin-bottom: 2.4rem;
}
> .tip {
font-family: pingfang_regular;
font-size: 1.4rem;
color: #999;
margin-bottom: 3rem;
> span {
cursor: pointer;
text-decoration: underline;
color: #0080ed;
}
}
> .btns {
display: flex;
justify-content: flex-end;
gap: 1.2rem;
> button {
padding: 0 2.4rem;
height: 4rem;
border-radius: 4rem;
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
font-family: pingfang_heavy;
font-size: 1.2rem;
color: #fff;
background: #000000;
border: none;
cursor: pointer;
&:active {
opacity: 0.8;
}
}
}
}
> .stop > .content {
> .btns {
> button {
background: #df2c2c;
}
}
}
}
}
</style>

View File

@@ -4,7 +4,7 @@
<div
v-for="v in list"
:key="v.path"
:class="{ active: v.path === activePath }"
:class="{ active: new RegExp(`^${v.path}`).test(activePath) }"
@click="handleClick(v.path)"
>
<div class="icon"><svg-icon :name="v.icon" size="20" /></div>
@@ -21,6 +21,7 @@
import { ref, computed } from "vue"
import { useRoute, useRouter } from "vue-router"
import toolTipBox from "./toolTipBox.vue"
import myEvent from "@/tool/myEvents.js"
const route = useRoute()
const router = useRouter()
const visible = ref(false)
@@ -51,6 +52,14 @@
if (path === activePath.value) return
router.push(path)
}
onMounted(()=>{
myEvent.add('newListing',()=>{
visible.value = true
})
})
onUnmounted(()=>{
myEvent.remove('newListing')
})
</script>
<style scoped lang="less">
.seller-dashboard-index {

View File

@@ -13,7 +13,12 @@
:class="{
last: i === breadcrumbs.length - 1
}"
@click="() => router.push({ name: v.name })"
@click="
() => {
const index = -(breadcrumbs.length - i - 1)
if (index < 0) router.go(index)
}
"
>{{ v.title }}</span
>
<span class="icon" v-show="i < breadcrumbs.length - 1">

View File

@@ -4,7 +4,7 @@ import sellerToolTipImg1 from '@/assets/images/seller/sellerToolTip-1.png'
import sellerToolTipImg2 from '@/assets/images/seller/sellerToolTip-2.png'
import sellerToolTipImg3 from '@/assets/images/seller/sellerToolTip-3.png'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
const props = defineProps({
visible: {
type: Boolean,
@@ -14,9 +14,9 @@ const props = defineProps({
const emit = defineEmits([
'update:visible','close'
])
const router = useRouter()
const {t} = useI18n()
let data = reactive({
visible: props.visible,
stepList: [
{
img: sellerToolTipImg1,
@@ -44,6 +44,12 @@ const cleardata = ()=>{
emit('close', data.showAgain)
}
const getStarted = ()=>{
emit('update:visible', false)
router.push({path:'/home/seller/myListings/select'})
}
onMounted(()=>{
})
onUnmounted(()=>{
@@ -55,7 +61,7 @@ const { showAgain } = toRefs(data);
<div ref="toolTipBoxRef" class="toolTipBox" v-if="visible">
<a-modal
class="collection generalModel"
v-model:visible="data.visible"
v-model:visible="props.visible"
:footer="null"
:get-container="() => toolTipBoxRef"
width="126.2rem"
@@ -105,7 +111,7 @@ const { showAgain } = toRefs(data);
{{t('SellerToolTip.showAgain')}}
</label>
</div>
<div class="gallery_btn" @click="cleardata()">
<div class="gallery_btn" @click="getStarted">
{{t('SellerToolTip.GetStarted')}}
<i class="fi fi-rr-arrow-small-right"></i>
</div>
@@ -116,9 +122,9 @@ const { showAgain } = toRefs(data);
</template>
<style lang="less" scoped>
.toolTipBox{
width: 100%;
height: 100%;
position: relative;
// width: 100%;
// height: 100%;
// position: relative;
:deep(.generalModel){
height: auto;
.ant-modal-body{