fetch(add): 账户管理

This commit is contained in:
sexygoat
2026-01-23 17:18:24 +08:00
parent 339abca4c0
commit b53fea43c6
93 changed files with 7094 additions and 3153 deletions

View File

@@ -31,6 +31,9 @@
:pageSize="pagination.pageSize"
:total="pagination.total"
:marginTop="10"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
:default-expand-all="false"
:pagination="false"
@selection-change="handleSelectionChange"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@@ -62,8 +65,24 @@
<ElRow :gutter="20" v-if="dialogType === 'add'">
<ElCol :span="12">
<ElFormItem label="上级店铺ID" prop="parent_id">
<ElInputNumber v-model="formData.parent_id" :min="1" placeholder="一级店铺可不填" style="width: 100%" clearable />
<ElFormItem label="上级店铺" prop="parent_id">
<ElSelect
v-model="formData.parent_id"
placeholder="一级店铺可不选"
filterable
remote
:remote-method="searchParentShops"
:loading="parentShopLoading"
clearable
style="width: 100%"
>
<ElOption
v-for="shop in parentShopList"
:key="shop.id"
:label="shop.shop_name"
:value="shop.id"
/>
</ElSelect>
</ElFormItem>
</ElCol>
</ElRow>
@@ -102,7 +121,11 @@
</ElCol>
<ElCol :span="12">
<ElFormItem label="联系电话" prop="contact_phone">
<ElInput v-model="formData.contact_phone" placeholder="请输入联系电话" maxlength="11" />
<ElInput
v-model="formData.contact_phone"
placeholder="请输入联系电话"
maxlength="11"
/>
</ElFormItem>
</ElCol>
</ElRow>
@@ -118,14 +141,23 @@
</ElCol>
<ElCol :span="12">
<ElFormItem label="密码" prop="init_password">
<ElInput v-model="formData.init_password" type="password" placeholder="请输入初始账号密码" show-password />
<ElInput
v-model="formData.init_password"
type="password"
placeholder="请输入初始账号密码"
show-password
/>
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="20">
<ElCol :span="12">
<ElFormItem label="手机号" prop="init_phone">
<ElInput v-model="formData.init_phone" placeholder="请输入初始账号手机号" maxlength="11" />
<ElInput
v-model="formData.init_phone"
placeholder="请输入初始账号手机号"
maxlength="11"
/>
</ElFormItem>
</ElCol>
</ElRow>
@@ -142,7 +174,9 @@
<template #footer>
<div class="dialog-footer">
<ElButton @click="dialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleSubmit" :loading="submitLoading">提交</ElButton>
<ElButton type="primary" @click="handleSubmit" :loading="submitLoading"
>提交</ElButton
>
</div>
</template>
</ElDialog>
@@ -153,7 +187,15 @@
<script setup lang="ts">
import { h } from 'vue'
import { FormInstance, ElMessage, ElMessageBox, ElTag, ElSwitch } from 'element-plus'
import {
FormInstance,
ElMessage,
ElMessageBox,
ElTag,
ElSwitch,
ElSelect,
ElOption
} from 'element-plus'
import type { FormRules } from 'element-plus'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
@@ -169,6 +211,9 @@
const dialogVisible = ref(false)
const loading = ref(false)
const submitLoading = ref(false)
const parentShopLoading = ref(false)
const parentShopList = ref<ShopResponse[]>([])
const searchParentShopList = ref<ShopResponse[]>([])
// 定义表单搜索初始值
const initialSearchState = {
@@ -211,7 +256,7 @@
}
// 表单配置项
const searchFormItems: SearchFormItem[] = [
const searchFormItems = computed<SearchFormItem[]>(() => [
{
label: '店铺名称',
prop: 'shop_name',
@@ -231,12 +276,20 @@
}
},
{
label: '上级ID',
label: '上级店铺',
prop: 'parent_id',
type: 'input',
type: 'select',
options: searchParentShopList.value.map((shop) => ({
label: shop.shop_name,
value: shop.id
})),
config: {
clearable: true,
placeholder: '请输入上级店铺ID'
filterable: true,
remote: true,
remoteMethod: handleSearchParentShop,
loading: parentShopLoading.value,
placeholder: '请选择或搜索上级店铺'
}
},
{
@@ -267,15 +320,13 @@
placeholder: '请选择状态'
}
}
]
])
// 列配置
const columnOptions = [
{ label: 'ID', prop: 'id' },
{ label: '店铺名称', prop: 'shop_name' },
{ label: '店铺编号', prop: 'shop_code' },
{ label: '层级', prop: 'level' },
{ label: '上级ID', prop: 'parent_id' },
{ label: '所在地区', prop: 'region' },
{ label: '联系人', prop: 'contact_name' },
{ label: '联系电话', prop: 'contact_phone' },
@@ -286,19 +337,13 @@
// 显示对话框
const showDialog = (type: string, row?: ShopResponse) => {
dialogVisible.value = true
dialogType.value = type
// 重置表单验证状态
if (formRef.value) {
formRef.value.resetFields()
}
if (type === 'edit' && row) {
formData.id = row.id
formData.shop_name = row.shop_name
formData.shop_code = ''
formData.parent_id = null
formData.parent_id = undefined
formData.province = row.province || ''
formData.city = row.city || ''
formData.district = row.district || ''
@@ -313,7 +358,7 @@
formData.id = 0
formData.shop_name = ''
formData.shop_code = ''
formData.parent_id = null
formData.parent_id = undefined
formData.province = ''
formData.city = ''
formData.district = ''
@@ -325,6 +370,13 @@
formData.init_password = ''
formData.init_phone = ''
}
// 重置表单验证状态
nextTick(() => {
formRef.value?.clearValidate()
})
dialogVisible.value = true
}
// 删除店铺
@@ -350,11 +402,6 @@
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{
prop: 'id',
label: 'ID',
width: 80
},
{
prop: 'shop_name',
label: '店铺名称',
@@ -373,12 +420,6 @@
return h(ElTag, { type: 'info', size: 'small' }, () => `${row.level}`)
}
},
{
prop: 'parent_id',
label: '上级ID',
width: 100,
formatter: (row: ShopResponse) => row.parent_id || '-'
},
{
prop: 'region',
label: '所在地区',
@@ -388,6 +429,7 @@
if (row.province) parts.push(row.province)
if (row.city) parts.push(row.city)
if (row.district) parts.push(row.district)
if (row.address) parts.push(row.address)
return parts.length > 0 ? parts.join(' / ') : '-'
}
},
@@ -413,7 +455,7 @@
activeText: getStatusText(CommonStatus.ENABLED),
inactiveText: getStatusText(CommonStatus.DISABLED),
inlinePrompt: true,
'onUpdate:modelValue': (val: number) => handleStatusChange(row, val)
'onUpdate:modelValue': (val: string | number | boolean) => handleStatusChange(row, val as number)
})
}
},
@@ -451,7 +493,7 @@
id: 0,
shop_name: '',
shop_code: '',
parent_id: null as number | null,
parent_id: undefined as number | undefined,
province: '',
city: '',
district: '',
@@ -466,15 +508,76 @@
onMounted(() => {
getShopList()
loadParentShopList()
loadSearchParentShopList()
})
// 加载上级店铺列表(用于新增对话框,默认加载20条)
const loadParentShopList = async (shopName?: string) => {
parentShopLoading.value = true
try {
const params: any = {
page: 1,
pageSize: 20
}
if (shopName) {
params.shop_name = shopName
}
const res = await ShopService.getShops(params)
if (res.code === 0) {
parentShopList.value = res.data.items || []
}
} catch (error) {
console.error('获取上级店铺列表失败:', error)
} finally {
parentShopLoading.value = false
}
}
// 加载搜索栏上级店铺列表(默认加载20条)
const loadSearchParentShopList = async (shopName?: string) => {
try {
const params: any = {
page: 1,
pageSize: 20
}
if (shopName) {
params.shop_name = shopName
}
const res = await ShopService.getShops(params)
if (res.code === 0) {
searchParentShopList.value = res.data.items || []
}
} catch (error) {
console.error('获取上级店铺列表失败:', error)
}
}
// 搜索上级店铺(用于新增对话框)
const searchParentShops = (query: string) => {
if (query) {
loadParentShopList(query)
} else {
loadParentShopList()
}
}
// 搜索上级店铺(用于搜索栏)
const handleSearchParentShop = (query: string) => {
if (query) {
loadSearchParentShopList(query)
} else {
loadSearchParentShopList()
}
}
// 获取店铺列表
const getShopList = async () => {
loading.value = true
try {
const params = {
page: pagination.currentPage,
page_size: pagination.pageSize,
page: 1,
page_size: 9999, // 获取所有数据用于构建树形结构
shop_name: searchForm.shop_name || undefined,
shop_code: searchForm.shop_code || undefined,
parent_id: searchForm.parent_id,
@@ -483,7 +586,8 @@
}
const res = await ShopService.getShops(params)
if (res.code === 0) {
tableData.value = res.data.items || []
const items = res.data.items || []
tableData.value = buildTreeData(items)
pagination.total = res.data.total || 0
}
} catch (error) {
@@ -493,6 +597,33 @@
}
}
// 构建树形数据
const buildTreeData = (items: ShopResponse[]) => {
const map = new Map<number, ShopResponse & { children?: ShopResponse[] }>()
const tree: ShopResponse[] = []
// 先将所有项放入 map
items.forEach((item) => {
map.set(item.id, { ...item, children: [] })
})
// 构建树形结构
items.forEach((item) => {
const node = map.get(item.id)!
if (item.parent_id && map.has(item.parent_id)) {
// 有父节点,添加到父节点的 children 中
const parent = map.get(item.parent_id)!
if (!parent.children) parent.children = []
parent.children.push(node)
} else {
// 没有父节点或父节点不存在,作为根节点
tree.push(node)
}
})
return tree
}
const handleRefresh = () => {
getShopList()
}
@@ -512,19 +643,15 @@
{ required: true, message: '请输入店铺编号', trigger: 'blur' },
{ min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur' }
],
address: [
{ max: 255, message: '地址不能超过255个字符', trigger: 'blur' }
],
contact_name: [
{ max: 50, message: '联系人姓名不能超过50个字符', trigger: 'blur' }
],
address: [{ max: 255, message: '地址不能超过255个字符', trigger: 'blur' }],
contact_name: [{ max: 50, message: '联系人姓名不能超过50个字符', trigger: 'blur' }],
contact_phone: [
{ len: 11, message: '联系电话必须为 11 位', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式', trigger: 'blur' }
],
init_username: [
{ required: true, message: '请输入初始账号用户名', trigger: 'blur' },
{ min: 3, max: 50, message: '长度在 3 到 50 个字符', trigger: 'blur' }
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
init_password: [
{ required: true, message: '请输入初始账号密码', trigger: 'blur' },
@@ -611,7 +738,10 @@
// 先更新UI
row.status = newStatus
try {
await ShopService.updateShop(row.id, { status: newStatus })
await ShopService.updateShop(row.id, {
shop_name: row.shop_name,
status: newStatus
})
ElMessage.success('状态切换成功')
} catch (error) {
// 切换失败,恢复原状态