diff --git a/src/api/StoreDiaryAPI.ts b/src/api/StoreDiaryAPI.ts new file mode 100644 index 0000000..56e2f91 --- /dev/null +++ b/src/api/StoreDiaryAPI.ts @@ -0,0 +1,54 @@ +import { axios, type Page, type PageResult } from "timi-web-api"; +import type { StoreDiary } from "@/types/StoreDiary"; + +const BASE_URI = "/gao/store/diary"; + +export interface StoreDiaryListOptions { + storeId?: string; + beginCreatedAt: number; + endCreatedAt: number; +} + +/** + * 门店日记接口。 + * + * @param page 分页参数 + * @param options 门店和月份筛选条件 + */ +async function list(page: Page, options: StoreDiaryListOptions): Promise> { + return await axios.post(`${BASE_URI}/list`, { + index: page.index, + size: page.size, + beginCreatedAt: options.beginCreatedAt, + endCreatedAt: options.endCreatedAt, + ...(options.storeId ? { storeId: options.storeId } : {}) + }); +} + +async function detail(id: string): Promise { + return await axios.post(`${BASE_URI}/detail`, undefined, { + params: { id } + }); +} + +async function create(article: StoreDiary): Promise { + return await axios.post(`${BASE_URI}/create`, article); +} + +async function update(article: StoreDiary): Promise { + await axios.post(`${BASE_URI}/update`, article); +} + +async function remove(id: string): Promise { + await axios.post(`${BASE_URI}/delete`, undefined, { + params: { id } + }); +} + +export default { + list, + detail, + create, + update, + remove +}; diff --git a/src/pages/store/StoreDiaryDetail.vue b/src/pages/store/StoreDiaryDetail.vue new file mode 100644 index 0000000..c6fffaa --- /dev/null +++ b/src/pages/store/StoreDiaryDetail.vue @@ -0,0 +1,268 @@ + + + + + diff --git a/src/pages/store/StoreDiaryEdit.vue b/src/pages/store/StoreDiaryEdit.vue new file mode 100644 index 0000000..4106e19 --- /dev/null +++ b/src/pages/store/StoreDiaryEdit.vue @@ -0,0 +1,322 @@ + + + + + diff --git a/src/pages/store/StoreDiaryList.vue b/src/pages/store/StoreDiaryList.vue new file mode 100644 index 0000000..5349c90 --- /dev/null +++ b/src/pages/store/StoreDiaryList.vue @@ -0,0 +1,505 @@ + + + + + diff --git a/src/pages/tabs/StoreTab.vue b/src/pages/tabs/StoreTab.vue index ccfa0bb..104f76d 100644 --- a/src/pages/tabs/StoreTab.vue +++ b/src/pages/tabs/StoreTab.vue @@ -34,6 +34,19 @@ + + + @@ -79,6 +92,9 @@ const canManageStore = computed(() => userStore.hasRoles([ gaoRoleCodes.globalManager, gaoRoleCodes.storeManager ], "or") && userStore.hasPermissions(permissionCodes.store.read)); +const canManageStoreDiary = computed(() => userStore.hasRoles(gaoRoleCodes.storeManager) + && !userStore.hasRoles(gaoRoleCodes.globalManager) + && userStore.hasPermissions(permissionCodes.storeDiary.read)); // ---------- 门店切换 ---------- @@ -138,6 +154,20 @@ async function goStoreManagement(): Promise { await router.push(`/management/stores/${store.id}`); } +async function goStoreDiaryManagement(): Promise { + if (!canManageStoreDiary.value) { + return; + } + + const store = await currentStoreStore.loadLoginStore(); + if (!store?.id) { + Message.warning("未绑定所属门店"); + return; + } + + await router.push({ name: "StoreDiaryList" }); +} + onMounted(async () => { navBarStore.setRightIcon("refresh", refreshCharts); if (canSelectStore.value) { diff --git a/src/router/index.ts b/src/router/index.ts index 96ac9be..b169a13 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -124,12 +124,23 @@ router.beforeEach(async (to) => { async function resolveStoreAccessRedirect(to: RouteLocationNormalized): Promise { const userStore = useUserStore(); + const routeName = String(to.name || ""); + const storeDiaryRouteNameList = ["StoreDiaryList", "StoreDiaryCreate", "StoreDiaryDetail", "StoreDiaryEdit"]; + if (storeDiaryRouteNameList.includes(routeName)) { + if (!userStore.hasRoles(gaoRoleCodes.storeManager) || userStore.hasRoles(gaoRoleCodes.globalManager)) { + return "/management"; + } + const currentStoreStore = useCurrentStoreStore(); + const store = await currentStoreStore.loadLoginStore(); + return store?.id ? undefined : "/management"; + } + if (!userStore.hasRoles(gaoRoleCodes.storeManager) || userStore.hasRoles(gaoRoleCodes.globalManager)) { return undefined; } const storeRouteNameList = ["StoreList", "StoreCreate", "StoreSort", "StoreEdit", "StoreBusinessDayCalendar"]; - if (!storeRouteNameList.includes(String(to.name || ""))) { + if (!storeRouteNameList.includes(routeName)) { return undefined; } diff --git a/src/router/routes/managementRoutes.ts b/src/router/routes/managementRoutes.ts index a5aa4f8..d60e32c 100644 --- a/src/router/routes/managementRoutes.ts +++ b/src/router/routes/managementRoutes.ts @@ -11,6 +11,9 @@ import StoreBusinessDayCalendar from "@/pages/store/StoreBusinessDayCalendar.vue import StoreEdit from "@/pages/store/StoreEdit.vue"; import StoreList from "@/pages/store/StoreList.vue"; import StoreSort from "@/pages/store/StoreSort.vue"; +import StoreDiaryDetail from "@/pages/store/StoreDiaryDetail.vue"; +import StoreDiaryEdit from "@/pages/store/StoreDiaryEdit.vue"; +import StoreDiaryList from "@/pages/store/StoreDiaryList.vue"; import TagList from "@/pages/tag/TagList.vue"; export const storeRoutes: RouteRecordRaw[] = [ @@ -83,6 +86,62 @@ export const storeRoutes: RouteRecordRaw[] = [ bodyBackground: "#F5F5F8", darkBodyBackground: "#10161f" } + }, + { + path: "/management/store-diaries", + name: "StoreDiaryList", + component: StoreDiaryList, + meta: { + depth: 3, + navBarVisible: true, + navBarCanBack: true, + navBarTitle: "门店日记", + tabBarVisible: false, + bodyBackground: "#F5F5F8", + darkBodyBackground: "#10161f" + } + }, + { + path: "/management/store-diaries/create", + name: "StoreDiaryCreate", + component: StoreDiaryEdit, + meta: { + depth: 4, + navBarVisible: true, + navBarCanBack: true, + navBarTitle: "新增门店日记", + tabBarVisible: false, + bodyBackground: "#F5F5F8", + darkBodyBackground: "#10161f" + } + }, + { + path: "/management/store-diaries/:id", + name: "StoreDiaryDetail", + component: StoreDiaryDetail, + meta: { + depth: 4, + navBarVisible: true, + navBarCanBack: true, + navBarTitle: "门店日记详情", + tabBarVisible: false, + bodyBackground: "#FFF", + darkBodyBackground: "#000" + } + }, + { + path: "/management/store-diaries/:id/edit", + name: "StoreDiaryEdit", + component: StoreDiaryEdit, + meta: { + depth: 5, + navBarVisible: true, + navBarCanBack: true, + navBarTitle: "编辑门店日记", + tabBarVisible: false, + bodyBackground: "#F5F5F8", + darkBodyBackground: "#10161f" + } } ]; diff --git a/src/types/StoreDiary.ts b/src/types/StoreDiary.ts new file mode 100644 index 0000000..e03085f --- /dev/null +++ b/src/types/StoreDiary.ts @@ -0,0 +1,28 @@ +import type { + Article as ArticleEditorArticle, + ArticleContentType, + Attachment as ArticleAttachment +} from "timi-tdesign-mobile"; +import type { User } from "timi-web-api"; + +/** 门店日记正文类型。 */ +export type StoreDiaryContentType = ArticleContentType; + +/** 门店日记附件。 */ +export type StoreDiaryAttachment = ArticleAttachment; + +/** 门店日记实体。 */ +export type StoreDiary = ArticleEditorArticle & { + id?: string; + bizType?: "GAO_STORE_DIARY"; + bizId?: string; + title?: string; + description?: string; + contentType?: StoreDiaryContentType; + content?: string; + attachmentList?: StoreDiaryAttachment[]; + status?: "DART" | "NORMAL" | "HIDDEN"; + createdAt?: number; + updatedAt?: number; + updater?: User; +}; diff --git a/src/utils/permission.ts b/src/utils/permission.ts index 30ae4d9..305b396 100644 --- a/src/utils/permission.ts +++ b/src/utils/permission.ts @@ -6,6 +6,12 @@ export const permissionCodes = { update: "GAO:STORE:UPDATE", delete: "GAO:STORE:DELETE" }, + storeDiary: { + create: "GAO:STORE_DIARY:CREATE", + read: "GAO:STORE_DIARY:READ", + update: "GAO:STORE_DIARY:UPDATE", + delete: "GAO:STORE_DIARY:DELETE" + }, customer: { create: "GAO:CUSTOMER:CREATE", read: "GAO:CUSTOMER:READ", @@ -112,6 +118,10 @@ export const gaoStoreScopePermissionCodes = [ permissionCodes.customer.export, permissionCodes.customer.update, permissionCodes.customer.delete, + permissionCodes.storeDiary.create, + permissionCodes.storeDiary.read, + permissionCodes.storeDiary.update, + permissionCodes.storeDiary.delete, permissionCodes.event.create, permissionCodes.event.read, permissionCodes.event.update,