update AIChart, customer followup, media selector

This commit is contained in:
Timi
2026-09-18 17:38:55 +08:00
parent c6b398fa98
commit 57c4275a23
57 changed files with 3866 additions and 2071 deletions
+4 -4
View File
@@ -9,7 +9,7 @@
<t-cell title="状态" :note="getProductStatusText(product.status)" />
<t-cell title="备注" :note="Text.display(product.remark)"></t-cell>
<t-cell>
<photo-scroller
<media-selector
:model-value="attachmentUrlList"
:photo-items="attachmentItemList"
title="附件列表"
@@ -118,7 +118,7 @@
<t-cell v-if="selectedSku.stockEnabled" title="最低库存" :note="`${getProductQuantityText(selectedSku.minStockQuantity)} 基础单位`" />
<t-cell v-if="selectedSku.remark" title="备注" :note="Text.display(selectedSku.remark)" />
</t-cell-group>
<photo-scroller
<media-selector
v-if="skuPhotoItemList.length"
:model-value="skuPhotoUrlList"
:photo-items="skuPhotoItemList"
@@ -154,7 +154,7 @@ import { BottomPopup } from "timi-tdesign-mobile";
import { useUserStore } from "timi-web-api";
import ProductAPI from "@/api/ProductAPI";
import NavBarActionMenu from "@/components/NavBarActionMenu.vue";
import PhotoScroller from "@/components/PhotoScroller.vue";
import MediaSelector from "@/components/mediaSelector/MediaSelector.vue";
import ProductProfile from "@/pages/product/component/ProductProfile.vue";
import { useNavBarStore } from "@/store/navBarStore";
import type { NavBarAction } from "@/types/NavBarAction";
@@ -435,7 +435,7 @@ onUnmounted(() => navBarStore.clearRight());
margin: 0 0 .75rem;
}
:deep(.photo-scroller) {
:deep(.media-selector) {
margin: 0 .75rem .75rem;
}
+21 -6
View File
@@ -23,7 +23,7 @@
<t-textarea v-model="form.remark" borderless maxlength="255" autosize placeholder="可填写产品说明" />
</t-form-item>
<t-form-item label-width="0">
<photo-scroller
<media-selector
v-model="attachmentUrlList"
v-model:photo-items="attachmentItemList"
title="产品公共图片及附件"
@@ -31,7 +31,9 @@
:loading="isAttachmentUploading"
:show-viewer-actions="false"
:add-actions="attachmentAddActions"
@add-photo="openAttachmentPicker('PHOTO')"
@add="openAttachmentPicker('OTHER')"
@capture-photo="capturePhoto"
@capture-video="captureVideo"
@add-other="openAttachmentPicker('OTHER')"
/>
</t-form-item>
@@ -197,7 +199,9 @@
>
<t-color-picker v-if="coverColorPickerVisible" v-model="coverColorDraft" format="HEX" type="multiple" :swatch-colors="colorSwatchList" @palette-bar-change="updateCoverColorDraftFromPalette" />
</bottom-popup>
<input ref="attachmentInputRef" class="file-input" type="file" accept="image/*" @change="handleAttachmentSelected" />
<input ref="attachmentInputRef" class="file-input" type="file" accept="*/*" @change="handleAttachmentSelected" />
<input ref="photoInputRef" class="file-input" type="file" accept="image/*" capture="environment" @change="handleAttachmentSelected" />
<input ref="videoInputRef" class="file-input" type="file" accept="video/*" capture="environment" @change="handleAttachmentSelected" />
</div>
</template>
@@ -206,7 +210,7 @@ import { BottomPopup } from "timi-tdesign-mobile";
import { DialogPlugin, type ColorObject } from "tdesign-mobile-vue";
import { type Attachment, AttachmentAPI, useUserStore } from "timi-web-api";
import ProductAPI from "@/api/ProductAPI";
import PhotoScroller from "@/components/PhotoScroller.vue";
import MediaSelector from "@/components/mediaSelector/MediaSelector.vue";
import ProductSkuEditor from "@/pages/product/component/ProductSkuEditor.vue";
import ProductSkuUnitEditor from "@/pages/product/component/ProductSkuUnitEditor.vue";
import ProductSkuUnitBatchEditor from "@/pages/product/component/ProductSkuUnitBatchEditor.vue";
@@ -252,6 +256,8 @@ const typePickerVisible = ref(false);
const categoryPickerVisible = ref(false);
const statusPickerVisible = ref(false);
const attachmentInputRef = ref<HTMLInputElement>();
const photoInputRef = ref<HTMLInputElement>();
const videoInputRef = ref<HTMLInputElement>();
const attachmentPickerTarget = ref<ProductAttachmentType>("PHOTO");
const isAttachmentUploading = ref(false);
const coverColorPickerVisible = ref(false);
@@ -269,7 +275,6 @@ const statusOptions = [{ label: "启用", value: "ACTIVE" }, { label: "停用",
const colorSwatchList = ["#2F88FF", "#0EA5E9", "#10B981", "#22C55E", "#F59E0B", "#F97316", "#EF4444", "#EC4899", "#8B5CF6", "#64748B"];
const selectedCategoryName = computed(() => categoryList.value.find(item => item.id === form.categoryId)?.name || "不设置分类");
const attachmentAddActions = [
{ icon: "camera", text: "产品图片", emit: "add-photo", theme: "primary" },
{ icon: "file-attachment", text: "其他文件", emit: "add-other", theme: "default" }
] as const;
const attachmentItemList = ref<ProductAttachmentItem[]>([]);
@@ -552,6 +557,16 @@ function openAttachmentPicker(target: ProductAttachmentType): void {
attachmentInputRef.value?.click();
}
function capturePhoto(): void {
attachmentPickerTarget.value = "PHOTO";
photoInputRef.value?.click();
}
function captureVideo(): void {
attachmentPickerTarget.value = "OTHER";
videoInputRef.value?.click();
}
function openCoverColorPicker(): void {
coverColorDraft.value = solidColorCoverColor.value || DEFAULT_COVER_COLOR;
coverColorPickerVisible.value = true;
@@ -628,7 +643,7 @@ async function handleAttachmentSelected(event: Event): Promise<void> {
try {
const tempFile = (await AttachmentAPI.upload(await compressUploadImage(file)))[0];
if (tempFile?.id) {
attachmentItemList.value.push({ attachType: attachmentPickerTarget.value, tempFileId: tempFile.id, url: AttachmentAPI.getTempReadURL(tempFile.id) });
attachmentItemList.value.push({ attachType: attachmentPickerTarget.value, mimeType: file.type, tempFileId: tempFile.id, url: AttachmentAPI.getTempReadURL(tempFile.id) });
}
} catch (error) {
Message.error(getErrorMessage(error, "产品附件上传失败"));
@@ -60,7 +60,7 @@
</div>
</t-form-item>
<t-form-item label-width="0">
<photo-scroller
<media-selector
:model-value="photoUrlList"
v-model:photo-items="photoItems"
:upload="uploadPhotoList"
@@ -99,7 +99,7 @@ import { watch } from "vue";
import { BottomPopup } from "timi-tdesign-mobile";
import { Time } from "timi-web";
import ProductAPI from "@/api/ProductAPI";
import PhotoScroller from "@/components/PhotoScroller.vue";
import MediaSelector from "@/components/mediaSelector/MediaSelector.vue";
import type {
Product,
ProductSku,
@@ -83,7 +83,7 @@
<span></span>
</template>
<template #description>
<photo-scroller
<media-selector
:model-value="photoURLList"
:photo-items="photoItemList"
:show-add="false"
@@ -119,7 +119,7 @@
<t-textarea v-model="revokeRemark" borderless autosize maxlength="500" placeholder="请输入冲正备注" />
</t-form-item>
</t-form>
<photo-scroller
<media-selector
:model-value="revokePhotoUrlList"
v-model:photo-items="revokePhotoItems"
:upload="uploadPhotoList"
@@ -137,7 +137,7 @@ import { BottomPopup } from "timi-tdesign-mobile";
import { PassTimeLabel } from "timi-web";
import { useUserStore, type Attachment } from "timi-web-api";
import ProductAPI from "@/api/ProductAPI";
import PhotoScroller from "@/components/PhotoScroller.vue";
import MediaSelector from "@/components/mediaSelector/MediaSelector.vue";
import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue";
import { useNavBarStore } from "@/store/navBarStore";
import type { PhotoItem } from "@/types/Photo";
@@ -106,7 +106,7 @@
<t-input v-model.trim="sku.remark" borderless maxlength="255" placeholder="选填" />
</t-form-item>
<div class="photo-wrap">
<photo-scroller
<media-selector
:model-value="photoUrlList"
v-model:photo-items="sku.photoItemList"
title="SKU 图片(选填)"
@@ -147,7 +147,7 @@
import { BottomPopup } from "timi-tdesign-mobile";
import type { ProductType } from "@/types/Product";
import type { ProductSkuForm, ProductSpecForm, ProductSkuUnitForm } from "@/types/ProductEditor";
import PhotoScroller from "@/components/PhotoScroller.vue";
import MediaSelector from "@/components/mediaSelector/MediaSelector.vue";
import { formatProductAmount, getProductSkuSpecEntries, parseProductAmount } from "@/utils/product";
import { uploadPhotoList } from "@/utils/photo";