|
|
@@ -1,31 +1,15 @@
|
|
|
<script setup lang="tsx">
|
|
|
-import { onMounted, reactive, ref, unref, nextTick, watch } from 'vue'
|
|
|
-import {
|
|
|
- getUserList,
|
|
|
- getUserGroup,
|
|
|
- getUserLevel,
|
|
|
- addUserData,
|
|
|
- // delUserData,
|
|
|
- putUserData,
|
|
|
- clearUserLevel
|
|
|
-} from '@/api/user'
|
|
|
-import type { userData } from '@/api/user/types'
|
|
|
+import { onMounted, reactive, ref, unref, watch } from 'vue'
|
|
|
+import { getProduct, getProductCategory } from '@/api/goods'
|
|
|
import { useI18n } from '@/hooks/web/useI18n'
|
|
|
import { Search } from '@/components/Search'
|
|
|
import { FormSchema } from '@/components/Form'
|
|
|
import { ContentWrap } from '@/components/ContentWrap'
|
|
|
-import Detail from './components/Detail.vue'
|
|
|
-import Write from './components/Write.vue'
|
|
|
-import { Dialog } from '@/components/Dialog'
|
|
|
import { BaseButton } from '@/components/Button'
|
|
|
import { userSearch } from '@/api/user/types'
|
|
|
import {
|
|
|
- ElSelect,
|
|
|
- ElOption,
|
|
|
- ElMessage,
|
|
|
+ ElSwitch,
|
|
|
ElDivider,
|
|
|
- ElText,
|
|
|
- ElMessageBox,
|
|
|
ElDropdown,
|
|
|
ElDropdownMenu,
|
|
|
ElDropdownItem,
|
|
|
@@ -34,17 +18,14 @@ import {
|
|
|
ElPagination
|
|
|
} from 'element-plus'
|
|
|
import { searchTime } from '@/utils/searchTime'
|
|
|
-import { formatToDate } from '@/utils/dateUtil'
|
|
|
import { TableImage } from '@/components/tableImage'
|
|
|
import BatchSet from './components/BatchSet.vue'
|
|
|
import { useSearch } from '@/hooks/web/useSearch'
|
|
|
import Icon from '@/components/Icon/src/Icon.vue'
|
|
|
-const { searchRegister, searchMethods } = useSearch()
|
|
|
-const { setSchema } = searchMethods
|
|
|
-searchTime.field = 'user_time'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+const { push } = useRouter()
|
|
|
+const { searchRegister } = useSearch()
|
|
|
const { t } = useI18n()
|
|
|
-const groupList = ref<any[]>([])
|
|
|
-const levelList = ref<any[]>([])
|
|
|
const currentPage = ref(1)
|
|
|
const pageSize = ref(10)
|
|
|
const total = ref(0)
|
|
|
@@ -52,54 +33,21 @@ const dataList = ref<any[]>([])
|
|
|
const loading = ref(false)
|
|
|
const getList = async () => {
|
|
|
loading.value = true
|
|
|
- const { data } = await getUserList({
|
|
|
+ const { data } = await getProduct({
|
|
|
page: unref(currentPage) || 1,
|
|
|
limit: unref(pageSize) || 10,
|
|
|
...unref(searchParams)
|
|
|
})
|
|
|
loading.value = false
|
|
|
- dataList.value = data.list
|
|
|
+ dataList.value = data.list.map((item) => {
|
|
|
+ item.is_show_bol = item.is_show == 1 ? true : false
|
|
|
+ return item
|
|
|
+ })
|
|
|
total.value = data.count
|
|
|
}
|
|
|
onMounted(async () => {
|
|
|
try {
|
|
|
getList()
|
|
|
- const [levelResponse, groupResponse] = await Promise.all([
|
|
|
- getUserLevel({ page: 1, limit: 999 }),
|
|
|
- getUserGroup({ page: 1, limit: 999 })
|
|
|
- ])
|
|
|
-
|
|
|
- // 处理 userLevel 数据
|
|
|
- const processLevelData = (data) =>
|
|
|
- data.list.map((res) => ({
|
|
|
- label: res.name,
|
|
|
- value: res.id
|
|
|
- }))
|
|
|
-
|
|
|
- // 处理 userGroup 数据
|
|
|
- const processGroupData = (data) =>
|
|
|
- data.list.map((res) => ({
|
|
|
- label: res.group_name,
|
|
|
- value: res.id
|
|
|
- }))
|
|
|
-
|
|
|
- levelList.value = processLevelData(levelResponse.data)
|
|
|
- groupList.value = processGroupData(groupResponse.data)
|
|
|
-
|
|
|
- await nextTick()
|
|
|
-
|
|
|
- setSchema([
|
|
|
- {
|
|
|
- field: 'level',
|
|
|
- path: 'componentProps.options',
|
|
|
- value: levelList.value
|
|
|
- },
|
|
|
- {
|
|
|
- field: 'group_id',
|
|
|
- path: 'componentProps.options',
|
|
|
- value: groupList.value
|
|
|
- }
|
|
|
- ])
|
|
|
} catch (error) {
|
|
|
console.error('Failed to fetch data:', error)
|
|
|
// 可以在这里添加更详细的错误处理逻辑,例如显示错误信息给用户
|
|
|
@@ -109,188 +57,28 @@ onMounted(async () => {
|
|
|
const searchSchema = reactive<FormSchema[]>([
|
|
|
{
|
|
|
field: 'nickname',
|
|
|
- label: '搜索',
|
|
|
- component: 'Input',
|
|
|
+ label: '商品分类',
|
|
|
+ component: 'Cascader',
|
|
|
componentProps: {
|
|
|
- placeholder: '姓名、手机、UID查询',
|
|
|
- slots: {
|
|
|
- prepend: () => {
|
|
|
- return (
|
|
|
- <>
|
|
|
- <ElSelect
|
|
|
- v-model={searchParams.value.field_key}
|
|
|
- placeholder="全部"
|
|
|
- style={'width: 80px'}
|
|
|
- >
|
|
|
- <ElOption label="全部" value="all" />
|
|
|
- <ElOption label="昵称" value="nickname" />
|
|
|
- <ElOption label="手机" value="phone" />
|
|
|
- <ElOption label="UID" value="uid" />
|
|
|
- </ElSelect>
|
|
|
- </>
|
|
|
- )
|
|
|
+ placeholder: '请选择商品分类',
|
|
|
+ props: {
|
|
|
+ lazy: true,
|
|
|
+ lazyLoad(node, resolve) {
|
|
|
+ getProductCategory({ page: 1, limit: 100, pid: node.value || -1 }).then((res) => {
|
|
|
+ console.log(res)
|
|
|
+ const nodes = res.data.map((item) => {
|
|
|
+ return {
|
|
|
+ value: item.id,
|
|
|
+ label: item.name,
|
|
|
+ leaf: !item.hasChildren
|
|
|
+ }
|
|
|
+ })
|
|
|
+ resolve(nodes)
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
- {
|
|
|
- field: 'status',
|
|
|
- label: '用户状态',
|
|
|
- component: 'Select',
|
|
|
- componentProps: {
|
|
|
- placeholder: '全部',
|
|
|
- options: [
|
|
|
- {
|
|
|
- label: '全部',
|
|
|
- value: ''
|
|
|
- },
|
|
|
- {
|
|
|
- label: '封禁',
|
|
|
- value: 0
|
|
|
- },
|
|
|
- {
|
|
|
- label: '正常',
|
|
|
- value: 1
|
|
|
- }
|
|
|
- ]
|
|
|
- }
|
|
|
- },
|
|
|
- {
|
|
|
- field: 'level',
|
|
|
- label: '用户等级',
|
|
|
- component: 'Select',
|
|
|
- componentProps: {
|
|
|
- placeholder: '全部'
|
|
|
- }
|
|
|
- },
|
|
|
- {
|
|
|
- field: 'group_id',
|
|
|
- label: '用户分组',
|
|
|
- component: 'Select',
|
|
|
- componentProps: {
|
|
|
- placeholder: '全部'
|
|
|
- }
|
|
|
- },
|
|
|
- {
|
|
|
- field: 'sex',
|
|
|
- label: '性别',
|
|
|
- value: '',
|
|
|
- component: 'Select',
|
|
|
- componentProps: {
|
|
|
- placeholder: '全部',
|
|
|
- options: [
|
|
|
- {
|
|
|
- label: '全部',
|
|
|
- value: ''
|
|
|
- },
|
|
|
- {
|
|
|
- label: '保密',
|
|
|
- value: 0
|
|
|
- },
|
|
|
- {
|
|
|
- label: '男',
|
|
|
- value: 1
|
|
|
- },
|
|
|
- {
|
|
|
- label: '女',
|
|
|
- value: 2
|
|
|
- }
|
|
|
- ]
|
|
|
- }
|
|
|
- },
|
|
|
- {
|
|
|
- field: 'is_promoter',
|
|
|
- label: '推广权限',
|
|
|
- component: 'Select',
|
|
|
- componentProps: {
|
|
|
- placeholder: '全部',
|
|
|
-
|
|
|
- options: [
|
|
|
- {
|
|
|
- label: '全部',
|
|
|
- value: ''
|
|
|
- },
|
|
|
- {
|
|
|
- label: '关闭',
|
|
|
- value: 0
|
|
|
- },
|
|
|
- {
|
|
|
- label: '开启',
|
|
|
- value: 1
|
|
|
- }
|
|
|
- ]
|
|
|
- }
|
|
|
- },
|
|
|
- {
|
|
|
- field: 'now_money',
|
|
|
- label: '余额排序',
|
|
|
- component: 'Select',
|
|
|
- componentProps: {
|
|
|
- placeholder: '默认',
|
|
|
- options: [
|
|
|
- {
|
|
|
- label: '默认',
|
|
|
- value: ''
|
|
|
- },
|
|
|
- {
|
|
|
- label: '降序',
|
|
|
- value: 'desc'
|
|
|
- },
|
|
|
- {
|
|
|
- label: '升序',
|
|
|
- value: 'asc'
|
|
|
- }
|
|
|
- ]
|
|
|
- }
|
|
|
- },
|
|
|
- // {
|
|
|
- // field: 'country',
|
|
|
- // label: '国家',
|
|
|
- // component: 'Select',
|
|
|
- // value: '',
|
|
|
- // componentProps: {
|
|
|
- // options: [
|
|
|
- // {
|
|
|
- // label: '全部',
|
|
|
- // value: ''
|
|
|
- // },
|
|
|
- // {
|
|
|
- // label: '国内',
|
|
|
- // value: 'domestic'
|
|
|
- // },
|
|
|
- // {
|
|
|
- // label: '国外',
|
|
|
- // value: 'abroad'
|
|
|
- // }
|
|
|
- // ]
|
|
|
- // }
|
|
|
- // },
|
|
|
- {
|
|
|
- field: 'user_time_type',
|
|
|
- label: '时间筛选类型',
|
|
|
- value: 'all',
|
|
|
- component: 'Select',
|
|
|
- componentProps: {
|
|
|
- options: [
|
|
|
- {
|
|
|
- label: '全部',
|
|
|
- value: 'all'
|
|
|
- },
|
|
|
- {
|
|
|
- label: '访问时间不在',
|
|
|
- value: 'visitno'
|
|
|
- },
|
|
|
- {
|
|
|
- label: '访问时间',
|
|
|
- value: 'visit'
|
|
|
- },
|
|
|
- {
|
|
|
- label: '添加时间',
|
|
|
- value: 'add_time'
|
|
|
- }
|
|
|
- ]
|
|
|
- }
|
|
|
- },
|
|
|
searchTime
|
|
|
])
|
|
|
|
|
|
@@ -315,114 +103,16 @@ const setSearchParams = (data: any) => {
|
|
|
getList()
|
|
|
}
|
|
|
|
|
|
-const dialogVisible = ref(false)
|
|
|
-const currentRow = ref()
|
|
|
-const dialogTitle = ref('')
|
|
|
-const actionType = ref('')
|
|
|
-const writeRef = ref<ComponentRef<typeof Write>>()
|
|
|
-const saveLoading = ref(false)
|
|
|
-const action = async (row: any, type: string) => {
|
|
|
- dialogTitle.value = t('exampleDemo.edit')
|
|
|
- actionType.value = type
|
|
|
- currentRow.value = row.uid
|
|
|
- dialogVisible.value = true
|
|
|
-}
|
|
|
-
|
|
|
-const AddAction = () => {
|
|
|
- dialogTitle.value = t('exampleDemo.add')
|
|
|
- currentRow.value = undefined
|
|
|
- dialogVisible.value = true
|
|
|
- actionType.value = ''
|
|
|
-}
|
|
|
-
|
|
|
-const delLevel = async (uid: any) => {
|
|
|
- ElMessageBox.confirm('清除后无法恢复,是否清除用户等级?', {
|
|
|
- confirmButtonText: '清除',
|
|
|
- cancelButtonText: '取消',
|
|
|
- type: 'warning'
|
|
|
- })
|
|
|
- .then(async () => {
|
|
|
- const res = await clearUserLevel(uid)
|
|
|
- if (res.status === 200) {
|
|
|
- ElMessage.success(res.msg)
|
|
|
- getList()
|
|
|
- }
|
|
|
- })
|
|
|
- .catch(() => {})
|
|
|
+const action = async (type: string, id?: number) => {
|
|
|
+ push(`/goods/edit/${type}/${id || 0}`)
|
|
|
}
|
|
|
|
|
|
-// const delAction = (row: any) => {
|
|
|
-// ElMessageBox.confirm('删除后无法恢复,是否删除用户?', {
|
|
|
-// confirmButtonText: '删除',
|
|
|
-// cancelButtonText: '取消',
|
|
|
-// type: 'warning'
|
|
|
-// })
|
|
|
-// .then(async () => {
|
|
|
-// const re = await delUserData(row.uid)
|
|
|
-// if (re) {
|
|
|
-// ElMessage({
|
|
|
-// showClose: true,
|
|
|
-// message: '删除成功',
|
|
|
-// type: 'success'
|
|
|
-// })
|
|
|
-// }
|
|
|
-// await getList()
|
|
|
-// })
|
|
|
-// .catch(() => {})
|
|
|
+// const drawerUser = ref(false)
|
|
|
+// const drawerUserId = ref()
|
|
|
+// const detail = (item) => {
|
|
|
+// drawerUser.value = true
|
|
|
+// drawerUserId.value = item.uid
|
|
|
// }
|
|
|
-const save = async () => {
|
|
|
- const write = unref(writeRef)
|
|
|
- const formData = await write?.submit()
|
|
|
- if (formData) {
|
|
|
- saveLoading.value = true
|
|
|
- const data: userData = {
|
|
|
- uid: formData.uid,
|
|
|
- real_name: formData.real_name,
|
|
|
- phone: formData.phone,
|
|
|
- pwd: formData.pwd,
|
|
|
- true_pwd: formData.true_pwd,
|
|
|
- trade_pwd: formData.trade_pwd,
|
|
|
- true_trade_pwd: formData.true_trade_pwd,
|
|
|
- sex: formData.sex,
|
|
|
- spread_uid: formData.spread_uid,
|
|
|
- is_promoter: formData.is_promoter ? 1 : 0,
|
|
|
- card_id: formData.card_id,
|
|
|
- birthday: formatToDate(formData.birthday),
|
|
|
- mark: formData.mark,
|
|
|
- status: formData.status ? 1 : 0,
|
|
|
- level: formData.level,
|
|
|
- group_id: formData.group_id
|
|
|
- }
|
|
|
- try {
|
|
|
- let res: any = {}
|
|
|
- // console.log(formData, 'formData')
|
|
|
- if (actionType.value === 'edit') {
|
|
|
- res = await putUserData(data)
|
|
|
- } else if (actionType.value === '') {
|
|
|
- res = await addUserData(data)
|
|
|
- }
|
|
|
- if (res?.status == 200) {
|
|
|
- ElMessage({
|
|
|
- showClose: true,
|
|
|
- message: '保存成功',
|
|
|
- type: 'success'
|
|
|
- })
|
|
|
- getList()
|
|
|
- dialogVisible.value = false
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.log(error)
|
|
|
- } finally {
|
|
|
- saveLoading.value = false
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-const drawerUser = ref(false)
|
|
|
-const drawerUserId = ref()
|
|
|
-const detail = (item) => {
|
|
|
- drawerUser.value = true
|
|
|
- drawerUserId.value = item.uid
|
|
|
-}
|
|
|
const selectChange = (res) => {
|
|
|
selectList.value = res
|
|
|
}
|
|
|
@@ -438,6 +128,12 @@ watch(currentPage, (val) => {
|
|
|
getList()
|
|
|
}
|
|
|
})
|
|
|
+const changeStatus = async (val: any, id: number) => {
|
|
|
+ console.log(val, id, 'val')
|
|
|
+}
|
|
|
+const delData = (res) => {
|
|
|
+ console.log(res)
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
@@ -448,34 +144,38 @@ watch(currentPage, (val) => {
|
|
|
@reset="setSearchParams"
|
|
|
@search="setSearchParams"
|
|
|
@register="searchRegister"
|
|
|
- show-expand
|
|
|
buttonPosition="right"
|
|
|
- expand-field="status"
|
|
|
/>
|
|
|
</div>
|
|
|
<div class="mb-10px">
|
|
|
- <BaseButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</BaseButton>
|
|
|
+ <BaseButton type="primary" @click="action('add')">{{ t('exampleDemo.add') }}</BaseButton>
|
|
|
<BaseButton @click="allSet" :disabled="selectList.length == 0">批量设置</BaseButton>
|
|
|
</div>
|
|
|
- <ElTable @selection-change="selectChange" :loading="loading" node-key="uid" :data="dataList">
|
|
|
+ <ElTable @selection-change="selectChange" :loading="loading" node-key="id" :data="dataList">
|
|
|
<ElTableColumn type="selection" prop="selection" />
|
|
|
- <ElTableColumn prop="uid" headerAlign="center" align="center" label="ID" width="70" />
|
|
|
- <ElTableColumn prop="avatar" headerAlign="center" align="center" label="头像" width="80">
|
|
|
+ <ElTableColumn prop="id" headerAlign="center" align="center" label="ID" width="70" />
|
|
|
+ <ElTableColumn prop="avatar" headerAlign="center" align="center" label="商品图片" width="80">
|
|
|
<template #default="{ row }">
|
|
|
- <TableImage :src="row.avatar" alt="头像" />
|
|
|
+ <TableImage :src="row.image" alt="商品图片" />
|
|
|
</template>
|
|
|
</ElTableColumn>
|
|
|
- <ElTableColumn prop="nickname" label="昵称" min-width="120">
|
|
|
+ <ElTableColumn prop="name" label="商品名称" minWidth="180" />
|
|
|
+ <ElTableColumn prop="price" label="商品售价" minWidth="80" />
|
|
|
+ <ElTableColumn prop="stock" label="库存" minWidth="80" />
|
|
|
+ <ElTableColumn prop="sales" label="销量" minWidth="80" />
|
|
|
+ <ElTableColumn prop="is_show" label="上下架" minWidth="80">
|
|
|
<template #default="{ row }">
|
|
|
- {{ row.nickname }}<br />
|
|
|
- <ElText v-if="row.delete_time" type="danger">{{ row.delete }}</ElText>
|
|
|
+ <ElSwitch
|
|
|
+ active-text="上架"
|
|
|
+ inactive-text="下架"
|
|
|
+ inline-prompt
|
|
|
+ :active-value="1"
|
|
|
+ :inactive-value="0"
|
|
|
+ v-model="row.is_show"
|
|
|
+ @change="(val) => changeStatus(val, row.id)"
|
|
|
+ />
|
|
|
</template>
|
|
|
</ElTableColumn>
|
|
|
- <ElTableColumn prop="vip_name" label="等级" min-width="80" />
|
|
|
- <ElTableColumn prop="group_id" label="分组" min-width="80" />
|
|
|
- <ElTableColumn prop="phone" label="手机号" minWidth="120" />
|
|
|
- <ElTableColumn prop="spread_uid_nickname" label="推荐人" min-width="80" />
|
|
|
- <ElTableColumn prop="now_money" label="余额" min-width="80" />
|
|
|
<ElTableColumn
|
|
|
prop="action"
|
|
|
label="操作"
|
|
|
@@ -486,13 +186,10 @@ watch(currentPage, (val) => {
|
|
|
fixed="right"
|
|
|
>
|
|
|
<template #default="{ row }">
|
|
|
- <BaseButton link size="small" type="primary" @click="action(row, 'edit')">
|
|
|
+ <BaseButton link size="small" type="primary" @click="action('edit', row.id)">
|
|
|
编辑
|
|
|
</BaseButton>
|
|
|
- <template v-if="!row.delete_time">
|
|
|
- <ElDivider direction="vertical" />
|
|
|
- <BaseButton link size="small" type="success" @click="detail(row)"> 详情 </BaseButton>
|
|
|
- </template>
|
|
|
+ <BaseButton link size="small" type="primary" @click="delData(row)" />
|
|
|
<ElDivider direction="vertical" />
|
|
|
<ElDropdown>
|
|
|
<BaseButton link size="small" type="info">
|
|
|
@@ -500,9 +197,9 @@ watch(currentPage, (val) => {
|
|
|
</BaseButton>
|
|
|
<template #dropdown
|
|
|
><ElDropdownMenu>
|
|
|
- <ElDropdownItem @click="delLevel(row.uid)">清除等级</ElDropdownItem>
|
|
|
- </ElDropdownMenu></template
|
|
|
- >
|
|
|
+ <ElDropdownItem>移到回收站 </ElDropdownItem>
|
|
|
+ </ElDropdownMenu>
|
|
|
+ </template>
|
|
|
</ElDropdown>
|
|
|
</template>
|
|
|
</ElTableColumn>
|
|
|
@@ -518,34 +215,11 @@ watch(currentPage, (val) => {
|
|
|
</ContentWrap>
|
|
|
|
|
|
<BatchSet
|
|
|
- :group-list="groupList"
|
|
|
:where="searchParams"
|
|
|
v-model="dialogSet"
|
|
|
:select-list="selectList"
|
|
|
@confirm="getList()"
|
|
|
/>
|
|
|
- <Detail
|
|
|
- :level-list="levelList"
|
|
|
- :group-list="groupList"
|
|
|
- v-model="drawerUser"
|
|
|
- :uid="drawerUserId"
|
|
|
- @confirm="getList()"
|
|
|
- />
|
|
|
- <Dialog v-model="dialogVisible" max-height="500px" :title="dialogTitle" width="700px">
|
|
|
- <Write
|
|
|
- ref="writeRef"
|
|
|
- :current-row="currentRow"
|
|
|
- :level-list="levelList"
|
|
|
- :group-list="groupList"
|
|
|
- :type="actionType"
|
|
|
- />
|
|
|
- <template #footer>
|
|
|
- <BaseButton type="primary" :loading="saveLoading" @click="save">
|
|
|
- {{ t('exampleDemo.save') }}
|
|
|
- </BaseButton>
|
|
|
- <BaseButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</BaseButton>
|
|
|
- </template>
|
|
|
- </Dialog>
|
|
|
</template>
|
|
|
<style lang="less">
|
|
|
.searchBox {
|