feat(seller): 评价管理列表增强(回复状态/有无图片筛选、评价分级 Tab、会员头像列)及多页面样式优化(商品列表、部门管理、OSS 素材、运费模板、页头页脚等)

This commit is contained in:
pikachu1995@126.com
2026-10-27 16:05:36 +08:00
parent 700c709afd
commit b025ab804e
22 changed files with 948 additions and 627 deletions

View File

@@ -1,81 +1,37 @@
<template>
<div class="search">
<el-card>
<el-row class="operation">
<el-switch
v-model="strict"
active-text="级联"
inactive-text="单选"
style="margin-right: 5px"
/>
<el-button @click="addRoot">添加部门</el-button>
<el-button type="primary" @click="add">添加子部门</el-button>
<div class="operation">
<el-button type="primary" @click="openAdd('0')">添加部门</el-button>
<el-button @click="delAll">批量删除</el-button>
<el-button @click="getParentList">刷新</el-button>
</el-row>
<el-row type="flex" justify="start">
<el-col :md="8" :lg="8" :xl="6">
<el-alert type="info" show-icon :closable="false">
当前选择编辑:
<span class="select-title">{{ editTitle }}</span>
<a v-if="form.id" class="select-clear" @click="cancelSelect">取消选择</a>
</el-alert>
<div v-loading="loading" class="tree-bar" :style="{ maxHeight: maxHeight }">
<el-tree
ref="treeRef"
:data="data"
:props="treeProps"
node-key="id"
show-checkbox
highlight-current
default-expand-all
:check-strictly="!strict"
:current-node-key="currentNodeKey"
@node-click="onNodeClick"
@check="onCheckChange"
/>
</div>
</el-col>
<el-col :md="15" :lg="13" :xl="9" style="margin-left: 10px">
<el-form ref="formRef" :model="form" label-width="100px" :rules="formValidate">
<el-form-item label="部门名称" prop="title">
<el-input v-model="form.title" />
</el-form-item>
<el-form-item label="选择角色">
<el-select
v-model="selectedRole"
multiple
:loading="userLoading"
placeholder="请选择角色"
style="width: 100%"
>
<el-option
v-for="item in users"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="排序值" prop="sortOrder">
<el-tooltip placement="right" content="值越小越靠前,支持小数">
<el-input-number v-model="form.sortOrder" :max="1000" :min="0" />
</el-tooltip>
</el-form-item>
<el-form-item>
<el-button
type="primary"
style="margin-right: 5px"
:loading="submitLoading"
@click="submitEdit"
>
修改并保存
</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
<el-button @click="getList">刷新</el-button>
</div>
<el-table
v-loading="loading"
:data="data"
row-key="id"
border
default-expand-all
@selection-change="onSelectionChange"
>
<el-table-column type="selection" width="50" align="center" />
<el-table-column prop="title" label="部门名称" min-width="180" />
<el-table-column label="上级部门" min-width="140">
<template #default="{ row }">{{ parentTitle(row) }}</template>
</el-table-column>
<el-table-column prop="sortOrder" label="排序值" width="90" align="center" />
<el-table-column prop="createTime" label="创建时间" width="170" align="center" />
<el-table-column prop="updateTime" label="修改时间" width="170" align="center" />
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<a class="link-text" @click="openAdd(row.id)">添加子级</a>
<span class="op-split">|</span>
<a class="link-text" @click="openEdit(row)">编辑</a>
<span class="op-split">|</span>
<a class="link-text" @click="delOne(row)">删除</a>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog
@@ -83,30 +39,55 @@
:title="modalTitle"
width="500px"
:close-on-click-modal="false"
destroy-on-close
>
<el-form ref="formAddRef" :model="formAdd" label-width="85px" :rules="formValidate">
<div v-if="showParent">
<el-form-item label="上级部门:">{{ form.title }}</el-form-item>
</div>
<el-form ref="formRef" :model="form" label-width="85px" :rules="formValidate">
<el-form-item label="上级部门">
<el-tree-select
v-model="form.parentId"
:data="parentOptions"
:props="{ label: 'title', children: 'children' }"
node-key="id"
check-strictly
default-expand-all
style="width: 100%"
/>
</el-form-item>
<el-form-item label="部门名称" prop="title">
<el-input v-model="formAdd.title" />
<el-input v-model="form.title" placeholder="请输入部门名称" />
</el-form-item>
<el-form-item v-if="modalType === 1" label="选择角色">
<el-select
v-model="selectedRole"
multiple
:loading="roleLoading"
placeholder="请选择角色"
style="width: 100%"
>
<el-option
v-for="item in roles"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="排序值" prop="sortOrder">
<el-tooltip placement="right" content="值越小越靠前,支持小数">
<el-input-number v-model="formAdd.sortOrder" :max="1000" :min="0" />
<el-input-number v-model="form.sortOrder" :max="1000" :min="0" />
</el-tooltip>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="cancelAdd">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="submitAdd">提交</el-button>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { onMounted, ref } from "vue";
import { computed, onMounted, ref } from "vue";
import {
initDepartment,
addDepartment,
@@ -120,36 +101,24 @@ import { Message, Modal } from "@/utils/message";
defineOptions({ name: "store-department-manage" });
const treeRef = ref(null);
const formRef = ref(null);
const formAddRef = ref(null);
const loading = ref(true);
const maxHeight = ref("500px");
const strict = ref(true);
const userLoading = ref(false);
const modalVisible = ref(false);
const selectList = ref([]);
const selectCount = ref(0);
const showParent = ref(false);
const modalType = ref(0); // 0 添加 1 编辑
const modalTitle = ref("");
const editTitle = ref("");
const submitLoading = ref(false);
const roleLoading = ref(false);
const data = ref([]);
const roles = ref([]);
const selectedRole = ref([]);
const currentNodeKey = ref(null);
const treeProps = {
label: "title",
children: "children",
isLeaf: "isLeaf",
};
const selectedRows = ref([]);
const form = ref({
id: "",
parentId: "0",
title: "",
parentId: "",
parentTitle: "",
sortOrder: 0,
status: 0,
});
const formAdd = ref({});
const formValidate = {
title: [{ required: true, message: "名称不能为空", trigger: "blur" }],
sortOrder: [
@@ -161,23 +130,57 @@ const formValidate = {
},
],
};
const submitLoading = ref(false);
const data = ref([]);
const users = ref([]);
function init() {
getParentList();
// id -> title 映射,用于展示上级部门名称
const titleMap = computed(() => {
const map = {};
const walk = (list) => {
list.forEach((item) => {
map[item.id] = item.title;
if (item.children?.length) walk(item.children);
});
};
walk(data.value);
return map;
});
// 上级部门下拉树:根节点为“一级部门”;编辑时剔除自身及子级,防止循环挂级
const parentOptions = computed(() => {
const tree =
modalType.value === 1 ? pruneTree(data.value, form.value.id) : data.value;
return [{ id: "0", title: "一级部门", children: tree }];
});
function pruneTree(list, excludeId) {
return list
.filter((item) => item.id !== excludeId)
.map((item) => ({
...item,
children: item.children?.length
? pruneTree(item.children, excludeId)
: undefined,
}));
}
function getParentList() {
function parentTitle(row) {
if (row.parentId === "0" || row.parentId == null) return "-";
return titleMap.value[row.parentId] || "-";
}
function getList() {
loading.value = true;
initDepartment().then((res) => {
loading.value = false;
if (res.success) {
const list = Array.isArray(res.result) ? res.result : [];
data.value = bubbleSort(normalizeDepartmentTree(list));
}
});
initDepartment()
.then((res) => {
if (res.success) {
const list = Array.isArray(res.result) ? res.result : [];
data.value = sortTree(normalizeDepartmentTree(list));
}
})
.finally(() => {
loading.value = false;
});
}
function normalizeDepartmentTree(list) {
return list.map((item) => {
const node = { ...item };
@@ -189,90 +192,71 @@ function normalizeDepartmentTree(list) {
return node;
});
}
function onNodeClick(data) {
if (!data) {
cancelSelect();
return;
}
const row = JSON.parse(JSON.stringify(data));
for (const attr in row) {
if (row[attr] == null) {
row[attr] = "";
}
}
editTitle.value = row.title;
currentNodeKey.value = row.id;
userLoading.value = true;
getUserByDepartmentId(row.id).then((res) => {
const way = [];
res.result &&
res.result.forEach((item) => {
way.push(item.roleId);
});
selectedRole.value = way;
function sortTree(list) {
list.sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
list.forEach((item) => {
if (item.children?.length) sortTree(item.children);
});
return list;
}
getRoleList({
pageNumber: 1,
pageSize: 10000,
}).then((res) => {
userLoading.value = false;
if (res.success) {
users.value = res.result.records;
form.value = row;
}
});
function onSelectionChange(rows) {
selectedRows.value = rows;
}
function onCheckChange(_data, { checkedNodes }) {
selectList.value = checkedNodes;
selectCount.value = checkedNodes.length;
function openAdd(parentId) {
modalType.value = 0;
modalTitle.value = parentId === "0" ? "添加部门" : "添加子部门";
form.value = { id: "", parentId, title: "", sortOrder: 0, status: 0 };
modalVisible.value = true;
}
function bubbleSort(array) {
if (!Array.isArray(array)) return [];
const len = array.length;
if (len < 2) return array;
for (let i = 0; i < len; i++) {
for (let j = 0; j < i; j++) {
if (array[j].sortOrder > array[i].sortOrder) {
const temp = array[j];
array[j] = array[i];
array[i] = temp;
}
}
}
return array;
}
function cancelSelect() {
treeRef.value?.setCurrentKey(null);
currentNodeKey.value = null;
formRef.value.resetFields();
delete form.value.id;
editTitle.value = "";
function openEdit(row) {
modalType.value = 1;
modalTitle.value = "编辑部门";
form.value = {
id: row.id,
parentId: row.parentId ?? "0",
title: row.title,
sortOrder: row.sortOrder ?? 0,
status: 0,
};
selectedRole.value = [];
roleLoading.value = true;
modalVisible.value = true;
Promise.all([
getUserByDepartmentId(row.id),
getRoleList({ pageNumber: 1, pageSize: 10000 }),
]).then(([bindRes, roleRes]) => {
roleLoading.value = false;
if (bindRes.result) {
selectedRole.value = bindRes.result.map((item) => item.roleId);
}
if (roleRes.success) {
roles.value = roleRes.result.records;
}
});
}
function cancelAdd() {
modalVisible.value = false;
}
function handleReset() {
formRef.value.resetFields();
form.value.status = 0;
}
function submitEdit() {
formRef.value.validate((valid) => {
if (valid) {
if (!form.value.id) {
Message.warning("请先点击选择要修改的部门");
return;
}
const roleWay = [];
selectedRole.value.forEach((item) => {
roleWay.push({
departmentId: form.value.id,
roleId: item,
});
});
function handleSubmit() {
formRef.value.validate((valid) => {
if (!valid) return;
submitLoading.value = true;
if (modalType.value === 0) {
addDepartment(form.value).then((res) => {
submitLoading.value = false;
if (res.success) {
Message.success("添加成功");
modalVisible.value = false;
getList();
}
});
} else {
const roleWay = selectedRole.value.map((roleId) => ({
departmentId: form.value.id,
roleId,
}));
Promise.all([
editDepartment(form.value.id, form.value),
updateDepartmentRole(form.value.id, roleWay),
@@ -280,76 +264,48 @@ function submitEdit() {
submitLoading.value = false;
if (res[0].success) {
Message.success("编辑成功");
init();
modalVisible.value = false;
getList();
}
});
}
});
}
function submitAdd() {
formAddRef.value.validate((valid) => {
if (valid) {
submitLoading.value = true;
addDepartment(formAdd.value).then((res) => {
submitLoading.value = false;
function delOne(row) {
Modal.confirm({
title: "确认删除",
content: "删除该部门将同时删除其下级所有部门,确认删除?",
loading: true,
onOk: () => {
deleteDepartment(row.id).then((res) => {
Modal.remove();
if (res.success) {
Message.success("添加成功");
init();
modalVisible.value = false;
Message.success("删除成功");
getList();
}
});
}
},
});
}
function add() {
if (form.value.id == "" || form.value.id == null) {
Message.warning("请先点击选择一个部门");
return;
}
modalTitle.value = "添加子部门";
showParent.value = true;
formAdd.value = {
parentId: form.value.id,
sortOrder: 0,
status: 0,
};
modalVisible.value = true;
}
function addRoot() {
modalTitle.value = "添加一级部门";
showParent.value = false;
formAdd.value = {
parentId: 0,
sortOrder: 0,
status: 0,
};
modalVisible.value = true;
}
function delAll() {
if (selectCount.value <= 0) {
if (selectedRows.value.length === 0) {
Message.warning("您还未勾选要删除的数据");
return;
}
Modal.confirm({
title: "确认删除",
content:
"您确认要删除所选的 " + selectCount.value + " 条数据及其下级所有数据?",
content: `您确认要删除所选的 ${selectedRows.value.length} 条数据及其下级所有数据?`,
loading: true,
onOk: () => {
let ids = "";
selectList.value.forEach(function (e) {
ids += e.id + ",";
});
ids = ids.substring(0, ids.length - 1);
const ids = selectedRows.value.map((e) => e.id).join(",");
deleteDepartment(ids).then((res) => {
Modal.remove();
if (res.success) {
Message.success("删除成功");
selectList.value = [];
selectCount.value = 0;
cancelSelect();
init();
selectedRows.value = [];
getList();
}
});
},
@@ -357,12 +313,21 @@ function delAll() {
}
onMounted(() => {
const height = document.documentElement.clientHeight;
maxHeight.value = Number(height - 287) + "px";
init();
getList();
});
</script>
<style lang="scss" scoped>
@import "@/styles/tree-common.scss";
.operation {
margin-bottom: 12px;
}
.link-text {
color: #2d8cf0;
cursor: pointer;
text-decoration: none;
}
.op-split {
margin: 0 8px;
color: #dcdee2;
}
</style>