项目创建
This commit is contained in:
@@ -0,0 +1,344 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<ElRow>
|
||||
<ElCol :xs="24" :sm="12" :lg="6">
|
||||
<ElInput placeholder="部门名称"></ElInput>
|
||||
</ElCol>
|
||||
<div style="width: 12px"></div>
|
||||
<ElCol :xs="24" :sm="12" :lg="6" class="el-col2">
|
||||
<ElButton v-ripple>搜索</ElButton>
|
||||
<ElButton @click="showDialog('add')" v-ripple>新增角色</ElButton>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
|
||||
<ArtTable :data="roleList" index>
|
||||
<template #default>
|
||||
<ElTableColumn label="角色名称" prop="roleName" />
|
||||
<ElTableColumn label="角色编码" prop="roleCode" />
|
||||
<ElTableColumn label="描述" prop="des" />
|
||||
<ElTableColumn label="启用" prop="enable">
|
||||
<template #default="scope">
|
||||
<ElTag :type="scope.row.enable ? 'primary' : 'info'">
|
||||
{{ scope.row.enable ? '启用' : '禁用' }}
|
||||
</ElTag>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="创建时间" prop="date">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.date) }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn fixed="right" label="操作" width="200px">
|
||||
<template #default="scope">
|
||||
<ElRow>
|
||||
<el-button link @click="showPermissionDialog()"> 菜单权限 </el-button>
|
||||
<el-button link @click="showDialog('edit', scope.row)"> 编辑 </el-button>
|
||||
<el-button link @click="deleteRole()"> 删除 </el-button>
|
||||
<!-- <ArtButtonMore
|
||||
:list="[
|
||||
{ key: 'permission', label: '菜单权限' },
|
||||
{ key: 'edit', label: '编辑角色' },
|
||||
{ key: 'delete', label: '删除角色' }
|
||||
]"
|
||||
@click="buttonMoreClick($event, scope.row)"
|
||||
/> -->
|
||||
</ElRow>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</template>
|
||||
</ArtTable>
|
||||
|
||||
<ElDialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogType === 'add' ? '新增角色' : '编辑角色'"
|
||||
width="30%"
|
||||
align-center
|
||||
>
|
||||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||||
<ElFormItem label="角色名称" prop="roleName">
|
||||
<ElInput v-model="form.roleName" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="角色编码" prop="roleCode">
|
||||
<ElInput v-model="form.roleCode" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="描述" prop="roleStatus">
|
||||
<ElInput v-model="form.des" type="textarea" :rows="3" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="启用">
|
||||
<ElSwitch v-model="form.enable" />
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="dialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleSubmit(formRef)">提交</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
|
||||
<ElDialog
|
||||
v-model="permissionDialog"
|
||||
title="菜单权限"
|
||||
width="520px"
|
||||
align-center
|
||||
class="el-dialog-border"
|
||||
>
|
||||
<ElScrollbar height="70vh">
|
||||
<ElTree
|
||||
ref="treeRef"
|
||||
:data="processedMenuList"
|
||||
show-checkbox
|
||||
node-key="name"
|
||||
:default-expand-all="isExpandAll"
|
||||
:default-checked-keys="[1, 2, 3]"
|
||||
:props="defaultProps"
|
||||
@check="handleTreeCheck"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<div style="display: flex; align-items: center">
|
||||
<span v-if="data.isAuth">
|
||||
{{ data.label }}
|
||||
</span>
|
||||
<span v-else>{{ defaultProps.label(data) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</ElTree>
|
||||
</ElScrollbar>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="toggleExpandAll">{{ isExpandAll ? '全部收起' : '全部展开' }}</ElButton>
|
||||
<ElButton @click="toggleSelectAll" style="margin-left: 8px">{{
|
||||
isSelectAll ? '取消全选' : '全部选择'
|
||||
}}</ElButton>
|
||||
<ElButton type="primary" @click="savePermission">保存</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useMenuStore } from '@/store/modules/menu'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { formatMenuTitle } from '@/router/utils/utils'
|
||||
// import { ButtonMoreItem } from '@/components/core/forms/ArtButtonMore.vue'
|
||||
import { Role, ROLE_LIST_DATA } from '@/mock/temp/formData'
|
||||
|
||||
defineOptions({ name: 'Role' })
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const permissionDialog = ref(false)
|
||||
const { menuList } = storeToRefs(useMenuStore())
|
||||
const treeRef = ref()
|
||||
const isExpandAll = ref(true)
|
||||
const isSelectAll = ref(false)
|
||||
|
||||
// 处理菜单数据,将 authList 转换为子节点
|
||||
const processedMenuList = computed(() => {
|
||||
const processNode = (node: any) => {
|
||||
const processed = { ...node }
|
||||
|
||||
// 如果有 authList,将其转换为子节点
|
||||
if (node.meta && node.meta.authList && node.meta.authList.length) {
|
||||
const authNodes = node.meta.authList.map((auth: any) => ({
|
||||
id: `${node.id}_${auth.auth_mark}`,
|
||||
name: `${node.name}_${auth.auth_mark}`,
|
||||
label: auth.title,
|
||||
auth_mark: auth.auth_mark,
|
||||
isAuth: true,
|
||||
checked: auth.checked || false
|
||||
}))
|
||||
|
||||
processed.children = processed.children ? [...processed.children, ...authNodes] : authNodes
|
||||
}
|
||||
|
||||
// 递归处理子节点
|
||||
if (processed.children) {
|
||||
processed.children = processed.children.map(processNode)
|
||||
}
|
||||
|
||||
return processed
|
||||
}
|
||||
|
||||
return menuList.value.map(processNode)
|
||||
})
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
name: [
|
||||
{ required: true, message: '请输入角色名称', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
des: [{ required: true, message: '请输入角色描述', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
const form = reactive<Role>({
|
||||
roleName: '',
|
||||
roleCode: '',
|
||||
des: '',
|
||||
date: '',
|
||||
enable: true
|
||||
})
|
||||
|
||||
const roleList = ref<Role[]>([])
|
||||
|
||||
onMounted(() => {
|
||||
getTableData()
|
||||
})
|
||||
|
||||
const getTableData = () => {
|
||||
roleList.value = ROLE_LIST_DATA
|
||||
}
|
||||
|
||||
const dialogType = ref('add')
|
||||
|
||||
const showDialog = (type: string, row?: any) => {
|
||||
dialogVisible.value = true
|
||||
dialogType.value = type
|
||||
|
||||
if (type === 'edit' && row) {
|
||||
form.roleName = row.roleName
|
||||
form.roleCode = row.roleCode
|
||||
form.des = row.des
|
||||
form.date = row.date
|
||||
form.enable = row.enable
|
||||
} else {
|
||||
form.roleName = ''
|
||||
form.roleCode = ''
|
||||
form.des = ''
|
||||
form.date = ''
|
||||
form.enable = true
|
||||
}
|
||||
}
|
||||
|
||||
// const buttonMoreClick = (item: ButtonMoreItem, row: any) => {
|
||||
// if (item.key === 'permission') {
|
||||
// showPermissionDialog()
|
||||
// } else if (item.key === 'edit') {
|
||||
// showDialog('edit', row)
|
||||
// } else if (item.key === 'delete') {
|
||||
// deleteRole()
|
||||
// }
|
||||
// }
|
||||
|
||||
const showPermissionDialog = () => {
|
||||
permissionDialog.value = true
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: (data: any) => formatMenuTitle(data.meta?.title) || ''
|
||||
}
|
||||
|
||||
const deleteRole = () => {
|
||||
ElMessageBox.confirm('确定删除该角色吗?', '删除确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'error'
|
||||
}).then(() => {
|
||||
ElMessage.success('删除成功')
|
||||
})
|
||||
}
|
||||
|
||||
const handleSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
const message = dialogType.value === 'add' ? '新增成功' : '修改成功'
|
||||
ElMessage.success(message)
|
||||
dialogVisible.value = false
|
||||
formEl.resetFields()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const savePermission = () => {
|
||||
ElMessage.success('权限保存成功')
|
||||
permissionDialog.value = false
|
||||
}
|
||||
|
||||
const toggleExpandAll = () => {
|
||||
const tree = treeRef.value
|
||||
if (!tree) return
|
||||
|
||||
// 使用store.nodesMap直接控制所有节点的展开状态
|
||||
const nodes = tree.store.nodesMap
|
||||
for (const node in nodes) {
|
||||
nodes[node].expanded = !isExpandAll.value
|
||||
}
|
||||
|
||||
isExpandAll.value = !isExpandAll.value
|
||||
}
|
||||
|
||||
const toggleSelectAll = () => {
|
||||
const tree = treeRef.value
|
||||
if (!tree) return
|
||||
|
||||
if (!isSelectAll.value) {
|
||||
// 全选:获取所有节点的key并设置为选中
|
||||
const allKeys = getAllNodeKeys(processedMenuList.value)
|
||||
tree.setCheckedKeys(allKeys)
|
||||
} else {
|
||||
// 取消全选:清空所有选中
|
||||
tree.setCheckedKeys([])
|
||||
}
|
||||
|
||||
isSelectAll.value = !isSelectAll.value
|
||||
}
|
||||
|
||||
const getAllNodeKeys = (nodes: any[]): string[] => {
|
||||
const keys: string[] = []
|
||||
const traverse = (nodeList: any[]) => {
|
||||
nodeList.forEach((node) => {
|
||||
if (node.name) {
|
||||
keys.push(node.name)
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
traverse(node.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
traverse(nodes)
|
||||
return keys
|
||||
}
|
||||
|
||||
const handleTreeCheck = () => {
|
||||
const tree = treeRef.value
|
||||
if (!tree) return
|
||||
|
||||
// 使用树组件的getCheckedKeys方法获取选中的节点
|
||||
const checkedKeys = tree.getCheckedKeys()
|
||||
const allKeys = getAllNodeKeys(processedMenuList.value)
|
||||
|
||||
// 判断是否全选:选中的节点数量等于总节点数量
|
||||
isSelectAll.value = checkedKeys.length === allKeys.length && allKeys.length > 0
|
||||
}
|
||||
|
||||
const formatDate = (date: string) => {
|
||||
return new Date(date)
|
||||
.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
})
|
||||
.replace(/\//g, '-')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-content {
|
||||
.svg-icon {
|
||||
width: 1.8em;
|
||||
height: 1.8em;
|
||||
overflow: hidden;
|
||||
vertical-align: -8px;
|
||||
fill: currentcolor;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,685 @@
|
||||
<template>
|
||||
<ArtTableFullScreen>
|
||||
<div class="account-page" id="table-full-screen">
|
||||
<!-- 搜索栏 -->
|
||||
<ArtSearchBar
|
||||
v-model:filter="formFilters"
|
||||
:items="formItems"
|
||||
@reset="handleReset"
|
||||
@search="handleSearch"
|
||||
></ArtSearchBar>
|
||||
|
||||
<ElCard shadow="never" class="art-table-card">
|
||||
<!-- 表格头部 -->
|
||||
<ArtTableHeader v-model:columns="columnChecks" @refresh="handleRefresh">
|
||||
<template #left>
|
||||
<ElButton @click="showDialog('add')">新增</ElButton>
|
||||
</template>
|
||||
</ArtTableHeader>
|
||||
|
||||
<!-- 表格 -->
|
||||
<ArtTable
|
||||
ref="tableRef"
|
||||
row-key="id"
|
||||
:loading="loading"
|
||||
:data="tableData"
|
||||
:currentPage="pagination.currentPage"
|
||||
:pageSize="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:marginTop="10"
|
||||
@selection-change="handleSelectionChange"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<template #default>
|
||||
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
||||
</template>
|
||||
</ArtTable>
|
||||
<ElDialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogType === 'add' ? '添加' : '编辑'"
|
||||
width="60%"
|
||||
align-center
|
||||
class="custom-dialog-scroll"
|
||||
style="max-height:80vh;overflow:auto;"
|
||||
>
|
||||
<ElForm ref="formRef" :model="formData" :rules="rules" label-width="100px">
|
||||
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
|
||||
<el-tab-pane label="基本信息" name="first">
|
||||
<el-row>
|
||||
<el-col :sm="24" :md="12">
|
||||
<ElFormItem label="产品名称" prop="name">
|
||||
<ElInput v-model="formData.name" />
|
||||
</ElFormItem>
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12">
|
||||
<ElFormItem label="封面图" prop="phone">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<el-upload
|
||||
ref="upload"
|
||||
v-model:file-list="fileList"
|
||||
class="upload-demo"
|
||||
action="https://usedcar.server.hnxinyouhe.com/console/upload/image"
|
||||
name="image"
|
||||
:on-success="handleCoverSuccess"
|
||||
:show-file-list="false"
|
||||
>
|
||||
<el-button type="primary">点击上传</el-button>
|
||||
</el-upload>
|
||||
<img
|
||||
v-if="coverImageUrl"
|
||||
:src="coverImageUrl"
|
||||
alt="封面图"
|
||||
style="width: 40px; height: 40px; margin-left: 16px; border-radius: 4px; object-fit: cover;"
|
||||
/>
|
||||
</div>
|
||||
</ElFormItem>
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12">
|
||||
<ElFormItem label="车辆识别码" prop="vin">
|
||||
<ElInput v-model="formData.vin" />
|
||||
</ElFormItem>
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12">
|
||||
<ElFormItem label="售价" prop="price">
|
||||
<ElInput v-model="formData.price" />
|
||||
</ElFormItem>
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12">
|
||||
<ElFormItem label="品牌" prop="brand">
|
||||
<ElSelect v-model="formData.brand" placeholder="请选择品牌" clearable>
|
||||
<ElOption
|
||||
v-for="brand in brandList"
|
||||
:key="brand.id"
|
||||
:value="brand.name"
|
||||
:label="brand.name"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12">
|
||||
<ElFormItem label="型号" prop="model">
|
||||
<ElInput v-model="formData.model" />
|
||||
</ElFormItem>
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12">
|
||||
<ElFormItem label="类型" prop="type">
|
||||
<ElInput v-model="formData.type" />
|
||||
</ElFormItem>
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12">
|
||||
<ElFormItem label="能源" prop="energy">
|
||||
<ElInput v-model="formData.energy" />
|
||||
</ElFormItem>
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12">
|
||||
<ElFormItem label="库存状态" prop="store_status">
|
||||
<ElSelect v-model="formData.store_status">
|
||||
<ElOption label="男" value="男" />
|
||||
<ElOption label="女" value="女" />
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12">
|
||||
<ElFormItem label="里程" prop="mileage">
|
||||
<ElInput v-model="formData.mileage" />
|
||||
</ElFormItem>
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12">
|
||||
<ElFormItem label="车龄(年)" prop="vehicle_age">
|
||||
<ElInput v-model="formData.vehicle_age" />
|
||||
</ElFormItem>
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12">
|
||||
<ElFormItem label="位置" prop="location">
|
||||
<ElSelect v-model="formData.location">
|
||||
<ElOption
|
||||
v-for="role in roleList"
|
||||
:key="role.roleCode"
|
||||
:value="role.roleCode"
|
||||
:label="role.roleName"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="24">
|
||||
<ElFormItem label="车况描述" prop="condition_desc">
|
||||
<ElInput v-model="formData.condition_desc" :rows="4" type="textarea" />
|
||||
</ElFormItem>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="车辆图册" name="second">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8" v-for="(url, part) in carAlbum" :key="part">
|
||||
<div style="margin-bottom: 16px; display: flex; flex-direction: column; align-items: center; justify-content: center;">
|
||||
<div style="margin-bottom: 8px; text-align: center;">{{ part }}</div>
|
||||
<el-upload
|
||||
:show-file-list="false"
|
||||
:action="'https://usedcar.server.hnxinyouhe.com/console/upload/image'"
|
||||
name="image"
|
||||
:on-success="(res, file) => handleAlbumSuccess(res, file, part)"
|
||||
>
|
||||
<el-button type="primary" size="small">上传</el-button>
|
||||
</el-upload>
|
||||
<img
|
||||
v-if="carAlbum[part]"
|
||||
:src="carAlbum[part]"
|
||||
style="display:block;width:60px;height:60px;margin-top:8px;object-fit:cover;border-radius:4px;"
|
||||
/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="dialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleSubmit">提交</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</ElCard>
|
||||
</div>
|
||||
</ArtTableFullScreen>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h } from 'vue'
|
||||
import { ROLE_LIST_DATA, ACCOUNT_TABLE_DATA } from '@/mock/temp/formData'
|
||||
|
||||
import { ElDialog, FormInstance, ElTag } from 'element-plus'
|
||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||
import type { FormRules } from 'element-plus'
|
||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
|
||||
import { UserService } from '@/api/usersApi'
|
||||
import { BrandService } from '@/api/carApi'
|
||||
import { SearchChangeParams, SearchFormItem } from '@/types'
|
||||
import type { TabsPaneContext } from 'element-plus'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const activeName = ref('first')
|
||||
|
||||
const handleClick = (tab: TabsPaneContext, event: Event) => {
|
||||
console.log(tab, event)
|
||||
}
|
||||
const { width } = useWindowSize()
|
||||
|
||||
defineOptions({ name: 'User' }) // 定义组件名称,用于 KeepAlive 缓存控制
|
||||
|
||||
const dialogType = ref('add')
|
||||
const dialogVisible = ref(false)
|
||||
const loading = ref(false)
|
||||
|
||||
// 定义表单搜索初始值
|
||||
const initialSearchState = {
|
||||
name: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
level: '',
|
||||
email: '',
|
||||
date: '',
|
||||
daterange: '',
|
||||
status: '1'
|
||||
}
|
||||
|
||||
const roleList = ref<any[]>([])
|
||||
const brandList = ref<any[]>([])
|
||||
|
||||
// 响应式表单数据
|
||||
const formFilters = reactive({ ...initialSearchState })
|
||||
|
||||
const pagination = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 20,
|
||||
total: 0
|
||||
})
|
||||
|
||||
// 表格数据
|
||||
const tableData = ref<any[]>([])
|
||||
|
||||
// 表格实例引用
|
||||
const tableRef = ref()
|
||||
|
||||
// 选中的行数据
|
||||
const selectedRows = ref<any[]>([])
|
||||
|
||||
// 重置表单
|
||||
const handleReset = () => {
|
||||
Object.assign(formFilters, { ...initialSearchState })
|
||||
pagination.currentPage = 1 // 重置到第一页
|
||||
getUserList()
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
console.log('搜索参数:', formFilters)
|
||||
pagination.currentPage = 1 // 搜索时重置到第一页
|
||||
getUserList()
|
||||
}
|
||||
|
||||
// 表单项变更处理
|
||||
const handleFormChange = (params: SearchChangeParams): void => {
|
||||
console.log('表单项变更:', params)
|
||||
}
|
||||
|
||||
// 表单配置项
|
||||
const formItems: SearchFormItem[] = [
|
||||
{
|
||||
label: '用户名',
|
||||
prop: 'name',
|
||||
type: 'input',
|
||||
config: {
|
||||
clearable: true
|
||||
},
|
||||
onChange: handleFormChange
|
||||
},
|
||||
|
||||
{
|
||||
label: '电话',
|
||||
prop: 'phone',
|
||||
type: 'input',
|
||||
config: {
|
||||
clearable: true
|
||||
},
|
||||
onChange: handleFormChange
|
||||
},
|
||||
{
|
||||
label: '用户等级',
|
||||
prop: 'level',
|
||||
type: 'select',
|
||||
config: {
|
||||
clearable: true
|
||||
},
|
||||
options: () => [
|
||||
{ label: '普通用户', value: 'normal' },
|
||||
{ label: 'VIP用户', value: 'vip' },
|
||||
{ label: '高级VIP', value: 'svip' },
|
||||
{ label: '企业用户', value: 'enterprise', disabled: true }
|
||||
],
|
||||
onChange: handleFormChange
|
||||
},
|
||||
{
|
||||
label: '地址',
|
||||
prop: 'address',
|
||||
type: 'input',
|
||||
config: {
|
||||
clearable: true
|
||||
},
|
||||
onChange: handleFormChange
|
||||
},
|
||||
{
|
||||
label: '邮箱',
|
||||
prop: 'email',
|
||||
type: 'input',
|
||||
config: {
|
||||
clearable: true
|
||||
},
|
||||
onChange: handleFormChange
|
||||
},
|
||||
// 支持 9 种日期类型定义
|
||||
// 具体可参考 src/components/core/forms/art-search-bar/widget/art-search-date/README.md
|
||||
{
|
||||
prop: 'date',
|
||||
label: '日期',
|
||||
type: 'date',
|
||||
config: {
|
||||
type: 'date',
|
||||
placeholder: '请选择日期'
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'daterange',
|
||||
label: '日期范围',
|
||||
type: 'daterange',
|
||||
config: {
|
||||
type: 'daterange',
|
||||
startPlaceholder: '开始时间',
|
||||
endPlaceholder: '结束时间'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'status',
|
||||
type: 'radio',
|
||||
options: [
|
||||
{ label: '在线', value: '1' },
|
||||
{ label: '离线', value: '2' }
|
||||
],
|
||||
onChange: handleFormChange
|
||||
}
|
||||
]
|
||||
|
||||
// 获取标签类型
|
||||
// 1: 在线 2: 离线 3: 异常 4: 注销
|
||||
const getTagType = (status: string) => {
|
||||
switch (status) {
|
||||
case '1':
|
||||
return 'success'
|
||||
case '2':
|
||||
return 'info'
|
||||
case '3':
|
||||
return 'warning'
|
||||
case '4':
|
||||
return 'danger'
|
||||
default:
|
||||
return 'info'
|
||||
}
|
||||
}
|
||||
|
||||
// 构建标签文本
|
||||
const buildTagText = (status: string) => {
|
||||
let text = ''
|
||||
if (status === '1') {
|
||||
text = '在线'
|
||||
} else if (status === '2') {
|
||||
text = '离线'
|
||||
} else if (status === '3') {
|
||||
text = '异常'
|
||||
} else if (status === '4') {
|
||||
text = '注销'
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
// 显示对话框
|
||||
const showDialog = (type: string, row?: any) => {
|
||||
dialogVisible.value = true
|
||||
dialogType.value = type
|
||||
|
||||
// 重置表单验证状态
|
||||
if (formRef.value) {
|
||||
formRef.value.resetFields()
|
||||
}
|
||||
|
||||
if (type === 'edit' && row) {
|
||||
formData.name = row.username
|
||||
} else {
|
||||
formData.name=''
|
||||
formData.cover=''
|
||||
formData.vin=''
|
||||
formData.price=''
|
||||
formData.brand=''
|
||||
formData.model=''
|
||||
formData.type=''
|
||||
formData.energy=''
|
||||
formData.store_status=''
|
||||
formData.mileage=''
|
||||
formData.vehicle_age=''
|
||||
formData.location=''
|
||||
formData.condition_desc=''
|
||||
}
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
const deleteUser = () => {
|
||||
ElMessageBox.confirm('确定要注销该用户吗?', '注销用户', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'error'
|
||||
}).then(() => {
|
||||
ElMessage.success('注销成功')
|
||||
})
|
||||
}
|
||||
|
||||
// 动态列配置
|
||||
const { columnChecks, columns } = useCheckedColumns(() => [
|
||||
{ type: 'selection' }, // 勾选列
|
||||
// { type: 'expand', label: '展开', width: 80 }, // 展开列
|
||||
// { type: 'index', label: '序号', width: 80 }, // 序号列
|
||||
{
|
||||
prop: 'avatar',
|
||||
label: '用户名',
|
||||
minWidth: width.value < 500 ? 220 : '',
|
||||
formatter: (row: any) => {
|
||||
return h('div', { class: 'user', style: 'display: flex; align-items: center' }, [
|
||||
h('img', { class: 'avatar', src: row.avatar }),
|
||||
h('div', {}, [
|
||||
h('p', { class: 'user-name' }, row.userName),
|
||||
h('p', { class: 'email' }, row.userEmail)
|
||||
])
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'userGender',
|
||||
label: '性别',
|
||||
sortable: true,
|
||||
formatter: (row) => (row.userGender === 1 ? '男' : '女')
|
||||
},
|
||||
{ prop: 'userPhone', label: '手机号' },
|
||||
{
|
||||
prop: 'status',
|
||||
label: '状态',
|
||||
formatter: (row) => {
|
||||
return h(ElTag, { type: getTagType(row.status) }, () => buildTagText(row.status))
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'createTime',
|
||||
label: '创建日期',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
prop: 'operation',
|
||||
label: '操作',
|
||||
width: 150,
|
||||
// fixed: 'right', // 固定在右侧
|
||||
// disabled: true,
|
||||
formatter: (row: any) => {
|
||||
return h('div', [
|
||||
h(ArtButtonTable, {
|
||||
type: 'edit',
|
||||
onClick: () => showDialog('edit', row)
|
||||
}),
|
||||
h(ArtButtonTable, {
|
||||
type: 'delete',
|
||||
onClick: () => deleteUser()
|
||||
})
|
||||
])
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
// 表单实例
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
name: '',
|
||||
cover: '',
|
||||
vin: '',
|
||||
price: '',
|
||||
brand: '',
|
||||
model: '',
|
||||
type: '',
|
||||
energy: '',
|
||||
store_status: '',
|
||||
mileage: '',
|
||||
vehicle_age: '',
|
||||
location: '',
|
||||
condition_desc: ''
|
||||
})
|
||||
|
||||
const fileList = ref<any[]>([])
|
||||
const coverImageUrl = ref('')
|
||||
|
||||
const handleCoverSuccess = (response: any, file: any) => {
|
||||
coverImageUrl.value = response.data.image_url || ''
|
||||
// 上传成功后清空 fileList,允许再次上传
|
||||
fileList.value = []
|
||||
}
|
||||
|
||||
defineExpose({ handleCoverSuccess })
|
||||
|
||||
onMounted(() => {
|
||||
getUserList()
|
||||
getRoleList()
|
||||
getBrandList()
|
||||
})
|
||||
|
||||
// 获取用户列表数据
|
||||
const getUserList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const { currentPage, pageSize } = pagination
|
||||
|
||||
const { records, current, size, total } = await UserService.getUserList({
|
||||
current: currentPage,
|
||||
size: pageSize
|
||||
})
|
||||
|
||||
// 使用本地头像替换接口返回的头像
|
||||
tableData.value = records.map((item: any, index: number) => ({
|
||||
...item,
|
||||
avatar: ACCOUNT_TABLE_DATA[index % ACCOUNT_TABLE_DATA.length].avatar
|
||||
}))
|
||||
|
||||
// 更新分页信息
|
||||
Object.assign(pagination, { currentPage: current, pageSize: size, total })
|
||||
} catch (error) {
|
||||
console.error('获取用户列表失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const getRoleList = () => {
|
||||
roleList.value = ROLE_LIST_DATA
|
||||
}
|
||||
|
||||
// 获取品牌列表
|
||||
const getBrandList = async () => {
|
||||
try {
|
||||
// 这里调用获取品牌列表的接口
|
||||
const response = await BrandService.getBrandInfo()
|
||||
brandList.value = response.data || []
|
||||
|
||||
// 临时使用模拟数据,实际使用时请替换为真实接口
|
||||
// brandList.value = [
|
||||
// { id: 1, name: '奔驰' },
|
||||
// { id: 2, name: '宝马' },
|
||||
// { id: 3, name: '奥迪' },
|
||||
// { id: 4, name: '大众' },
|
||||
// { id: 5, name: '丰田' },
|
||||
// { id: 6, name: '本田' },
|
||||
// { id: 7, name: '日产' },
|
||||
// { id: 8, name: '现代' },
|
||||
// { id: 9, name: '起亚' },
|
||||
// { id: 10, name: '福特' }
|
||||
// ]
|
||||
} catch (error) {
|
||||
console.error('获取品牌列表失败:', error)
|
||||
ElMessage.error('获取品牌列表失败')
|
||||
}
|
||||
}
|
||||
|
||||
const handleRefresh = () => {
|
||||
getUserList()
|
||||
}
|
||||
|
||||
// 处理表格行选择变化
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
selectedRows.value = selection
|
||||
}
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive<FormRules>({
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
phone: [
|
||||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式', trigger: 'blur' }
|
||||
],
|
||||
gender: [{ required: true, message: '请选择性别', trigger: 'change' }],
|
||||
role: [{ required: true, message: '请选择角色', trigger: 'change' }]
|
||||
})
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
await formRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
ElMessage.success(dialogType.value === 'add' ? '添加成功' : '更新成功')
|
||||
dialogVisible.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 处理表格分页变化
|
||||
const handleSizeChange = (newPageSize: number) => {
|
||||
pagination.pageSize = newPageSize
|
||||
getUserList()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (newCurrentPage: number) => {
|
||||
pagination.currentPage = newCurrentPage
|
||||
getUserList()
|
||||
}
|
||||
|
||||
const carAlbum = reactive<Record<string, string>>({
|
||||
'45度正脸': '',
|
||||
'左前45度': '',
|
||||
'左前大灯': '',
|
||||
'正前': '',
|
||||
'车头下部': ''
|
||||
// 可继续添加更多部位
|
||||
})
|
||||
|
||||
const handleAlbumSuccess = (response: any, file: any, part: string) => {
|
||||
carAlbum[part] = response.data?.image_url || ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.account-page {
|
||||
:deep(.user) {
|
||||
.avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
> div {
|
||||
margin-left: 10px;
|
||||
|
||||
.user-name {
|
||||
font-weight: 500;
|
||||
color: var(--art-text-gray-800);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.custom-dialog-scroll .el-dialog__body {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.avatar-uploader .el-upload {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user