diff --git a/src/components/PhotoScroller.vue b/src/components/PhotoScroller.vue
index 967a16c..b453ecc 100644
--- a/src/components/PhotoScroller.vue
+++ b/src/components/PhotoScroller.vue
@@ -22,6 +22,7 @@
/>
@@ -165,6 +166,7 @@ interface Props {
showViewerActions?: boolean;
showAdd?: boolean;
showRemove?: boolean;
+ showTypeLabel?: boolean;
submit?: (photoItems: PhotoItem[]) => Promise;
title?: string;
}
@@ -181,6 +183,7 @@ type ViewerPhotoItem = PhotoItem & {
};
const ATTACH_TYPE_TEXT_MAP: Record = {
+ ADDITIONAL: "附加照片",
PHOTO: "照片",
POINT_CARD: "积分卡",
OTHER: "其他文件"
@@ -196,6 +199,7 @@ const props = withDefaults(defineProps(), {
required: false,
showAdd: true,
showRemove: true,
+ showTypeLabel: true,
showViewerActions: true
});
diff --git a/src/pages/notify/NotifyCreate.vue b/src/pages/notify/NotifyCreate.vue
index 5e92e1e..73741d5 100644
--- a/src/pages/notify/NotifyCreate.vue
+++ b/src/pages/notify/NotifyCreate.vue
@@ -423,7 +423,12 @@ onUnmounted(() => navBarStore.clearRight());
}
:deep(.content) {
+ padding: 0;
overflow: visible;
+
+ .value {
+ border-radius: 0;
+ }
}
:deep(.toolbar) {
diff --git a/src/pages/store/StoreDiaryDetail.vue b/src/pages/store/StoreDiaryDetail.vue
index ee95a23..2a6dbe8 100644
--- a/src/pages/store/StoreDiaryDetail.vue
+++ b/src/pages/store/StoreDiaryDetail.vue
@@ -11,6 +11,18 @@
underline
/>
+
(() => (article.value?.attachmentList || [])
+ .filter(item => item.attachType === "ADDITIONAL" && !!item.id)
+ .map(item => ({
+ attachment: item as Partial,
+ attachType: "ADDITIONAL",
+ previewUrl: getAttachmentReadURL(item.id || ""),
+ url: getAttachmentReadURL(item.id || "", 90)
+ })));
+const additionalPhotoList = computed(() => additionalPhotoItemList.value.map(item => item.url));
+
function resolveAttachmentUrl(attachmentId: string): string {
return getAttachmentReadURL(attachmentId);
}
@@ -222,6 +246,12 @@ onUnmounted(() => navBarStore.clearRight());
}
}
+ .additional-photos-wrapper {
+ margin: 1rem 1rem 0 1rem;
+ border-bottom: 1px solid rgba(148, 163, 184, .2);
+ padding-bottom: 1rem;
+ }
+
.content {
padding: 1rem;
diff --git a/src/pages/store/StoreDiaryEdit.vue b/src/pages/store/StoreDiaryEdit.vue
index 4106e19..6573607 100644
--- a/src/pages/store/StoreDiaryEdit.vue
+++ b/src/pages/store/StoreDiaryEdit.vue
@@ -21,6 +21,26 @@
placeholder="可填写本篇日记摘要"
/>
+
();
+const photoItemList = ref([]);
+const isPhotoUploading = ref(false);
+const photoList = computed({
+ get: () => photoItemList.value.map(item => item.url),
+ set: value => {
+ const urlSet = new Set(value);
+ photoItemList.value = photoItemList.value.filter(item => urlSet.has(item.url));
+ }
+});
+
+function openPhotoPicker(): void {
+ photoInputRef.value?.click();
+}
+
+async function handlePhotoSelected(event: Event): Promise {
+ const input = event.target as HTMLInputElement;
+ const fileList = Array.from(input.files || []);
+ if (!fileList.length) {
+ return;
+ }
+
+ isPhotoUploading.value = true;
+ try {
+ const tempFileList = await AttachmentAPI.upload(await compressUploadImageList(fileList));
+ for (const tempFile of tempFileList) {
+ if (!tempFile.id) {
+ continue;
+ }
+ photoItemList.value.push({
+ tempFileId: tempFile.id,
+ attachType: "ADDITIONAL",
+ url: AttachmentAPI.getTempReadURL(tempFile.id)
+ });
+ }
+ } catch (error) {
+ Message.error(getErrorMessage(error, "附加照片上传失败"));
+ } finally {
+ isPhotoUploading.value = false;
+ input.value = "";
+ }
+}
+
+function buildPhotoItemList(attachmentList?: StoreDiary["attachmentList"]): PhotoItem[] {
+ return (attachmentList || []).flatMap(item => {
+ if (item.attachType !== "ADDITIONAL" || !item.id) {
+ return [];
+ }
+ return [{
+ attachment: item as Partial,
+ attachType: "ADDITIONAL",
+ previewUrl: getAttachmentReadURL(item.id),
+ url: getAttachmentReadURL(item.id, 90)
+ }];
+ });
+}
+
+function buildAdditionalAttachmentList(): Attachment[] {
+ return photoItemList.value.flatMap(item => {
+ if (item.attachment) {
+ return [{
+ ...item.attachment,
+ attachType: "ADDITIONAL"
+ }];
+ }
+ if (item.tempFileId) {
+ return [{
+ attachType: "ADDITIONAL",
+ tempFileId: item.tempFileId
+ }];
+ }
+ return [];
+ });
+}
+
const selectedStoreName = computed(() => {
const store = currentStoreStore.storeList.find(item => item.id === article.value.bizId)
|| (currentStoreStore.loginStore?.id === article.value.bizId ? currentStoreStore.loginStore : undefined);
@@ -187,7 +286,10 @@ async function save(): Promise {
description: article.value.description?.trim() || "",
contentType: normalizeContentType(editorValue?.contentType ?? article.value.contentType),
content: editorValue?.content ?? article.value.content ?? "",
- attachmentList: editorValue?.attachmentList ?? article.value.attachmentList ?? [],
+ attachmentList: [
+ ...(editorValue?.attachmentList ?? article.value.attachmentList ?? []).filter(item => item.attachType !== "ADDITIONAL"),
+ ...buildAdditionalAttachmentList()
+ ],
status: article.value.status || "NORMAL"
};
if (!validateArticle(value)) {
@@ -234,6 +336,7 @@ async function loadArticle(): Promise {
...createEmptyArticle(),
bizId: article.value.bizId || currentStoreStore.currentStoreId
};
+ photoItemList.value = [];
return;
}
@@ -246,6 +349,7 @@ async function loadArticle(): Promise {
content: result.content || "",
attachmentList: result.attachmentList || []
};
+ photoItemList.value = buildPhotoItemList(result.attachmentList);
}
onMounted(async () => {
@@ -278,9 +382,10 @@ onUnmounted(() => navBarStore.clearRight());
.editor-wrapper {
width: 100%;
+ overflow: visible;
min-width: 0;
max-width: 100%;
- overflow: visible;
+ border-top: 1px solid var(--td-component-border);
.editor {
width: 100%;
@@ -292,23 +397,28 @@ onUnmounted(() => navBarStore.clearRight());
:deep(.tp-editor) {
width: 100%;
+ height: auto;
min-width: 0;
max-width: 100%;
- height: auto;
overflow: visible;
}
:deep(.content) {
+ padding: 0;
overflow: visible;
+
+ .value {
+ border-radius: 0;
+ }
}
:deep(.toolbar) {
- width: 100%;
- min-width: 0;
- max-width: 100%;
top: var(--app-nav-height);
+ width: 100%;
z-index: 10;
position: sticky;
+ min-width: 0;
+ max-width: 100%;
}
:deep(.toolbar .scroll) {
@@ -317,6 +427,14 @@ onUnmounted(() => navBarStore.clearRight());
max-width: 100%;
}
}
+
+ .photo-wrapper {
+ padding: 1rem;
+
+ .file-input {
+ display: none;
+ }
+ }
}
}
diff --git a/src/pages/store/StoreDiaryList.vue b/src/pages/store/StoreDiaryList.vue
index 5349c90..5f5ff70 100644
--- a/src/pages/store/StoreDiaryList.vue
+++ b/src/pages/store/StoreDiaryList.vue
@@ -12,6 +12,7 @@
:key="calendarRenderKey"
class="calendar-view"
:value="selectedCalendarDate"
+ :min-date="calendarMinDate"
:format="calendarFormat"
switch-mode="month"
:use-popup="false"
@@ -26,17 +27,7 @@
class="item"
v-entrance-transition="index * 30"
>
-
-
-
-
-
+
@@ -87,10 +71,11 @@
import { BottomPopup } from "timi-tdesign-mobile";
import { type CalendarProps, DialogPlugin, type TDate } from "tdesign-mobile-vue";
import { type PageResult, useUserStore } from "timi-web-api";
-import { PassTimeLabel, Time } from "timi-web";
+import { Time } from "timi-web";
import StoreDiaryAPI, { type StoreDiaryListOptions } from "@/api/StoreDiaryAPI";
import { useCurrentStoreStore } from "@/store/currentStoreStore";
import { useNavBarStore } from "@/store/navBarStore";
+import StoreDiaryListItem from "@/pages/store/component/StoreDiaryListItem.vue";
import type { NavBarAction } from "@/types/NavBarAction";
import type { StoreDiary } from "@/types/StoreDiary";
import { getErrorMessage } from "@/utils/error";
@@ -102,6 +87,7 @@ defineOptions({
});
const router = useRouter();
+const route = useRoute();
const navBarStore = useNavBarStore();
const userStore = useUserStore();
const currentStoreStore = useCurrentStoreStore();
@@ -112,10 +98,12 @@ const canShowActionMenu = computed(() => canCreate.value);
// ---------- 日历 ----------
const today = new Date();
-const selectedCalendarDate = ref(today);
+const calendarMinDate = new Date(today.getFullYear() - 2, 0, 1);
+const initialCalendarDate = parseCalendarDate(route.query.calendarDate);
+const selectedCalendarDate = ref(initialCalendarDate);
const calendarMonth = reactive({
- year: today.getFullYear(),
- month: today.getMonth() + 1
+ year: initialCalendarDate.getFullYear(),
+ month: initialCalendarDate.getMonth() + 1
});
const calendarRenderKey = ref(0);
const calendarDiaryList = ref([]);
@@ -290,12 +278,24 @@ function getDiaryDateText(item: StoreDiary): string {
return date ? Time.formatDate(date) : "";
}
-function getTitle(item: StoreDiary): string {
- return item.title?.trim() || "未命名日记";
-}
-
-function getDescription(item: StoreDiary): string {
- return item.description?.trim() || "暂无摘要";
+function parseCalendarDate(value: unknown): Date {
+ const text = Array.isArray(value) ? String(value[0] || "") : String(value || "");
+ const [year, month, day] = text.split("-").map(Number);
+ const date = new Date(year, month - 1, day);
+ if (
+ !Number.isInteger(year)
+ || !Number.isInteger(month)
+ || !Number.isInteger(day)
+ || month < 1
+ || 12 < month
+ || day < 1
+ || date.getFullYear() !== year
+ || date.getMonth() + 1 !== month
+ || date.getDate() !== day
+ ) {
+ return today;
+ }
+ return date;
}
// ---------- 导航 ----------
@@ -347,6 +347,13 @@ async function toDetail(item: StoreDiary): Promise {
if (!item.id) {
return;
}
+ await router.replace({
+ name: "StoreDiaryList",
+ query: {
+ ...route.query,
+ calendarDate: getDiaryDateText(item) || Time.formatDate(selectedCalendarDate.value)
+ }
+ });
await router.push({
name: "StoreDiaryDetail",
params: { id: item.id }
@@ -363,7 +370,7 @@ async function confirmDelete(item: StoreDiary): Promise {
const diaryId = item.id;
const dialog = DialogPlugin.confirm({
title: "删除门店日记",
- content: `确定删除“${getTitle(item)}”吗?删除后无法恢复。`,
+ content: `确定删除“${item.title?.trim() || "未命名日记"}”吗?删除后无法恢复。`,
cancelBtn: "取消",
confirmBtn: "删除",
onCancel() {
@@ -463,28 +470,6 @@ onUnmounted(() => navBarStore.clearRight());
}
}
-
-.cell {
- :deep(.t-cell__title) {
- min-width: 0;
- overflow: hidden;
- }
-
- :deep(.t-cell__title-text),
- :deep(.t-cell__description) {
- overflow: hidden;
- min-width: 0;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
-
- :deep(.t-cell__note) {
- flex: 0 0 auto;
- min-width: 0;
- white-space: nowrap;
- }
-}
-
.diary-popup {
padding-bottom: .5rem;
diff --git a/src/pages/store/component/StoreDiaryListItem.vue b/src/pages/store/component/StoreDiaryListItem.vue
new file mode 100644
index 0000000..67c0aa7
--- /dev/null
+++ b/src/pages/store/component/StoreDiaryListItem.vue
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+