完善报价单填表与币种回填,并补齐账号末次登录、角色按钮权限与陆运全线匹配。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
linmianxun
2026-09-19 22:03:45 +08:00
co-authored by Cursor
parent 1a2e1d6cb2
commit e4743541f7
25 changed files with 2262 additions and 163 deletions
@@ -20,6 +20,11 @@ import ProtoIcon from "/@/views/inquiry/components/ProtoIcon.vue";
import AdminModal from "/@/views/inquiry/components/AdminModal.vue";
import AdminPagination from "/@/views/inquiry/components/AdminPagination.vue";
import { joinRoutes, parseRouteList } from "/@/views/inquiry/staffRole/staffRoutes";
import {
ROUTES_BY_ROLE,
dataPermTitle,
normalizeStaffRouteKeys,
} from "/@/views/inquiry/shared/dataPermCatalog";
type StaffRole = "sales" | "air" | "sea" | "land";
@@ -52,26 +57,7 @@ const roleLabelMap: Record<StaffRole, string> = {
land: "产品·陆运",
};
/** 按岗位的负责线路选项(与原型 admin-staff.html ROUTES_BY_ROLE 一致) */
const ROUTES_BY_ROLE: Record<Exclude<StaffRole, "sales">, string[]> = {
air: [
"CGO-KUL",
"SZX-KUL",
"HKG-KUL",
"NKG-KUL",
"HKG-DEL",
"CGO-MAA",
"CGO-BLR",
"ZUH-CRK",
"WNZ-CGK",
"BKK",
"外采-空运",
"全线",
],
sea: ["海运全线"],
land: ["中港/中亚/中欧", "国内长途/零担", "国内城配/拖车/打包", "东南亚"],
};
/** 负责线路与角色数据权限同源(Excel《数据权限下拉选项》) */
const ALL_ROUTE_FILTER_OPTIONS = [
...ROUTES_BY_ROLE.air,
...ROUTES_BY_ROLE.sea,
@@ -120,8 +106,11 @@ const form = reactive({
/** 当前岗位可选线路;销售为空 */
const routeOptions = computed(() => {
if (!isProductOrAir(form.role)) return [] as string[];
return ROUTES_BY_ROLE[form.role];
if (!isProductOrAir(form.role)) return [] as { label: string; value: string }[];
return ROUTES_BY_ROLE[form.role].map((key) => ({
value: key,
label: dataPermTitle(key),
}));
});
const showLeaderAndRoutes = computed(() => isProductOrAir(form.role));
@@ -367,7 +356,7 @@ watch(
return;
}
const valid = new Set(ROUTES_BY_ROLE[role]);
form.routeList = form.routeList.filter((r) => valid.has(r));
form.routeList = normalizeStaffRouteKeys(form.routeList).filter((r) => valid.has(r));
},
);
@@ -392,7 +381,10 @@ function openCreate() {
function openEdit(row: StaffRow) {
editing.value = row;
const valid = isProductOrAir(row.role) ? new Set(ROUTES_BY_ROLE[row.role]) : null;
const routes = parseRouteList(row.routes).filter((r) => (valid ? valid.has(r) : true));
// 旧 routes(全线/BKK/外采-空运 等)归一到 Excel 码后再过滤本岗选项
const routes = normalizeStaffRouteKeys(parseRouteList(row.routes)).filter((r) =>
valid ? valid.has(r) : true,
);
Object.assign(form, {
name: row.name,
wecomId: row.wecomId,
@@ -432,7 +424,9 @@ async function saveStaff() {
departName: form.dept.trim() || undefined,
roleCode: form.role,
roleLabel: roleLabelMap[form.role],
routes: isProductOrAir(form.role) ? joinRoutes(form.routeList) : undefined,
routes: isProductOrAir(form.role)
? joinRoutes(normalizeStaffRouteKeys(form.routeList))
: undefined,
isLeader: isProductOrAir(form.role) ? (form.isLeader === "true" ? 1 : 0) : null,
notifyEventException: form.notifyEventException ? 1 : 0,
status: form.status,
@@ -480,7 +474,7 @@ async function syncWecom() {
}
function routeTags(routes: string) {
return parseRouteList(routes);
return normalizeStaffRouteKeys(parseRouteList(routes)).map((k) => dataPermTitle(k));
}
function filterRouteOption(input: string, option: any) {
@@ -535,7 +529,7 @@ function filterRouteOption(input: string, option: any) {
</select>
<select v-model="filters.route">
<option value="">负责线路</option>
<option v-for="r in ALL_ROUTE_FILTER_OPTIONS" :key="r" :value="r">{{ r }}</option>
<option v-for="r in ALL_ROUTE_FILTER_OPTIONS" :key="r" :value="r">{{ dataPermTitle(r) }}</option>
</select>
<select v-model="filters.status">
<option value="">状态</option>
@@ -713,7 +707,7 @@ function filterRouteOption(input: string, option: any) {
show-search
class="staff-route-select"
placeholder="请选择负责线路,可多选"
:options="routeOptions.map((r) => ({ label: r, value: r }))"
:options="routeOptions"
:filter-option="filterRouteOption"
/>
</div>