merge(picker): resume sp-picker

This commit is contained in:
lukaijie
2026-05-19 10:01:45 +08:00
parent 12f603b16a
commit 83f2eb6dc0
2 changed files with 30 additions and 167 deletions

View File

@@ -70,26 +70,6 @@
height: 120px;
box-sizing: border-box;
background: 50% 50% / cover no-repeat rgb(221, 221, 221);
overflow: hidden;
&--video,
&--audio {
background: #2a2a2a;
}
.media-preview {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.audio-placeholder {
display: flex;
align-items: center;
justify-content: center;
color: #c0c4cc;
font-size: 36px;
background: linear-gradient(135deg, #36383d 0%, #1f2024 100%);
}
&:hover {
.image-meta {
display: block;
@@ -127,10 +107,6 @@
margin-top: 8px;
text-align: right;
}
.picker-source-tabs {
.el-tabs__nav-wrap::after { height: 1px; }
.el-tabs__header { margin-bottom: 0; }
}
.image-box-selected {
position: absolute;
box-sizing: border-box;
@@ -186,28 +162,24 @@
</style>
<template>
<div v-loading="loading" class="picker-image">
<div v-if="enableSourceTab" class="px-3 pt-2">
<el-tabs v-model="sourceTab" class="picker-source-tabs" @tab-click="onSourceTabChange">
<el-tab-pane label="原素材" name="normal" />
<el-tab-pane label="AI 素材" name="ai_generate" />
</el-tabs>
</div>
<div class="p-3 flex justify-between">
<div class="flex items-center gap-3">
<!-- AI tab 下不允许在 picker 现场上传AI 素材只能从制作页"保存到素材库"流入 -->
<SpImageUploader
v-if="sourceTab === 'normal'"
:localpost-data="localpostData"
:refresh="refresh"
:current-category="selectCatgory"
/>
<el-button @click="onAddGroup" class="h-[calc(1em+16px)]">
{{ addGroupBtnText }}
</el-button>
{{
addGroupBtnText
}}
</el-button>
<el-button :disabled="disabledBtn" @click="onMoveGroup">
{{ moveGroupBtnText }}
</el-button>
{{
moveGroupBtnText
}}
</el-button>
<!-- <el-button :disabled="disabledDeleteGroup" @click="onDeleteImageGroup">
删除分组
</el-button> -->
@@ -232,8 +204,10 @@
下载
</el-button> -->
<el-button :disabled="disabledBtn" @click="handleCancelAll">
{{ cancelAllBtnText }}
</el-button>
{{
cancelAllBtnText
}}
</el-button>
</div>
<!-- <div>
<el-input size="small" placeholder="请输入图片名称" suffix-icon="el-icon-search" />
@@ -280,35 +254,8 @@
>
<div
class="image-item relative group"
:class="{
'image-item--video': mediaKindOf(item) === 'video',
'image-item--audio': mediaKindOf(item) === 'audio'
}"
:style="mediaKindOf(item) === 'image' ? { color: '#fff', backgroundImage: `url('${item.url}')` } : { color: '#fff' }"
:style="{ color: '#fff', backgroundImage: `url('${item.url}')` }"
>
<!-- 视频缩略图浏览器读首帧时长由 onloadedmetadata 拿到塞到 item -->
<video
v-if="mediaKindOf(item) === 'video'"
:src="item.url"
muted
preload="metadata"
class="media-preview"
@loadedmetadata="onMediaMeta(item, $event)"
/>
<!-- 音频大图标占位 -->
<div v-else-if="mediaKindOf(item) === 'audio'" class="media-preview audio-placeholder">
<i class="el-icon-headset" />
</div>
<!-- 媒体类型角标 + 时长 -->
<div
v-if="mediaKindOf(item) !== 'image'"
class="absolute top-1 left-1 px-1 rounded text-[11px] leading-[16px] bg-black/50"
>
{{ mediaKindOf(item) === 'video' ? '视频' : '音频' }}
<span v-if="item._duration"> · {{ formatDuration(item._duration) }}</span>
</div>
<div
class="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200"
>
@@ -332,9 +279,7 @@
fill="white"
@click.stop="handleCopy(item.url)"
/>
<!-- 仅图片可裁剪 -->
<file-editing
v-if="mediaKindOf(item) === 'image'"
theme="outline"
size="20"
fill="white"
@@ -416,7 +361,9 @@
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="cropperDialogShow = false">{{ cancelText }}</el-button>
<el-button type="primary" @click="cropperDialogShow = false">{{ confirmText }}</el-button>
<el-button type="primary" @click="cropperDialogShow = false">{{
confirmText
}}</el-button>
</span>
</el-dialog>
</div>
@@ -460,16 +407,6 @@ export default {
columns: {
type: Number,
default: 6
},
/**
* 是否显示「原素材 / AI 素材」来源切换 tab。
* - 业务弹窗(如商品图选择)默认开启,便于跨库选图
* - 独立的素材管理页(/content/material/material-management不需要这个 tab
* AI 素材有专门的管理页(/ai-material/material-library素材管理页只负责原素材
*/
enableSourceTab: {
type: Boolean,
default: true
}
},
data() {
@@ -513,9 +450,6 @@ export default {
selected: data,
catgoryList: [],
selectCatgory: -1,
// 素材来源 tabnormal=原素材库 / ai_generate=AI素材库
// 通过 value.source 传入可初始定位(场景:从某些入口想直接打开 AI tab
sourceTab: (this.value && this.value.source === 'ai_generate') ? 'ai_generate' : 'normal',
groupDialog: false,
groupForm: {
groupId: '',
@@ -792,14 +726,12 @@ export default {
await this.$api.picker.addImageCatgory({
image_cat_id: groupId,
image_cat_name: groupName,
parent_id: 0,
source: this.sourceTab
parent_id: 0
})
} else {
await this.$api.picker.addImageCatgory({
image_cat_name: groupName,
parent_id: 0,
source: this.sourceTab
parent_id: 0
})
}
this.groupDialog = false
@@ -847,8 +779,7 @@ export default {
let params = {
type: 'image',
page: page_no,
pageSize: page_size,
source: this.sourceTab
pageSize: page_size
}
if (this.selectCatgory != '-1') {
params = {
@@ -861,14 +792,12 @@ export default {
return { count: total_count }
},
async getImageAllCatgory() {
const { list } = await this.$api.picker.getImageAllCatgory({
image_cat_id: 0,
source: this.sourceTab
})
const { list } = await this.$api.picker.getImageAllCatgory({ image_cat_id: 0 })
this.catgoryList = [
{ image_cat_id: -1, image_cat_name: i18n.t('4c4bec01.a8982a') },
...list.reverse()
]
console.log('catgoryList:', this.catgoryList)
this.editFormListOptions = this.catgoryList.map((item) => {
return {
title: item.image_cat_name,
@@ -876,39 +805,6 @@ export default {
}
})
},
/** 切换原素材 / AI 素材 tab分类树和列表都重拉分类选中重置 */
async onSourceTabChange() {
this.selectCatgory = -1
await this.getImageAllCatgory()
this.refresh(true)
},
/** 按 image_type MIME 前缀 / image_url 后缀分辨媒体类型 */
mediaKindOf(item) {
const mime = (item && item.image_type) ? String(item.image_type).toLowerCase() : ''
if (mime.indexOf('video/') === 0) return 'video'
if (mime.indexOf('audio/') === 0) return 'audio'
if (mime.indexOf('image/') === 0) return 'image'
// 兜底用扩展名
const url = String((item && (item.url || item.image_full_url || item.image_url)) || '')
const ext = (url.split('?')[0].split('.').pop() || '').toLowerCase()
if (['mp4', 'mov', 'webm', 'm4v'].indexOf(ext) > -1) return 'video'
if (['mp3', 'wav', 'm4a', 'aac', 'ogg', 'flac'].indexOf(ext) > -1) return 'audio'
return 'image'
},
/** 浏览器读完视频/音频元数据后塞 _duration 到 item复用既有响应式数组 */
onMediaMeta(item, ev) {
const dur = ev && ev.target && ev.target.duration
if (dur && isFinite(dur)) {
this.$set(item, '_duration', Math.round(dur))
}
},
formatDuration(sec) {
if (!sec) return ''
const s = Math.max(0, Math.round(Number(sec) || 0))
const m = Math.floor(s / 60)
const r = s % 60
return m + ':' + (r < 10 ? '0' + r : r)
},
handleClickCatgory({ image_cat_id }) {
this.selectCatgory = image_cat_id

View File

@@ -98,18 +98,6 @@
:form-list="applyFormList"
@onSubmit="onApplySubmit"
/>
<SpTranslatePopup
ref="translatePopup"
table-name="items"
:data-id="translateContext.dataId"
:fields="translateContext.fields"
:values="translateContext.values"
:source-language="translateContext.sourceLang"
@done="onTranslateDone"
@save-only="onTranslateSaveOnly"
@cancel="onTranslateCancel"
/>
</SpPage>
</template>
@@ -120,8 +108,6 @@ import GoodsParams from './components/GoodsParams'
import SpecParams from './components/SpecParams'
import SkuParams from './components/SkuParams'
import sku from '../../store/modules/sku'
import SpTranslatePopup from '@/components/sp-translate-popup'
import translateMixin from '@/mixins/translateMixin'
export default {
async beforeRouteLeave(to, from, next) {
if (this.$refs['decorateRef']?.dialogVisible) {
@@ -138,7 +124,6 @@ export default {
next()
}
},
mixins: [translateMixin],
data() {
const cascaderProps = {
props: {
@@ -866,7 +851,6 @@ export default {
submitLoading: false,
loading: false,
isLeave: false,
isShowPoint: false,
// 管理分类上绑定的规格
mainCategorySpec: [],
@@ -1616,47 +1600,30 @@ export default {
this.submitLoading = true
try {
const isUpdate = !!(itemId && !is_new)
let translateItemId = itemId
if (isUpdate) {
if (itemId && !is_new) {
await this.$api.goods.updateItems(itemId, {
...params,
item_id: itemId
})
this.$message.success(this.$t('de022579.55aa63'))
} else {
const res = await this.$api.goods.createItems(params)
translateItemId = (res && res.data && res.data.data && (res.data.data.item_id || res.data.data.id)) || 0
await this.$api.goods.createItems(params)
this.$message.success(this.$t('de022579.3fdaea'))
}
this.submitLoading = false
if (translateItemId) {
// 创建/编辑保持一致:弹「同步翻译」弹框;仅保存/取消由 mixin 跳回列表
this.openTranslate(translateItemId, ['item_name', 'brief', 'item_unit'], [this.form.itemName || '', this.form.brief || '', this.form.itemUnit || ''])
} else {
this.navigateBack()
this.isLeave = true
// 安全调用父组件方法
if (this.$parent && typeof this.$parent.onActivated === 'function') {
this.$parent.onActivated()
}
setTimeout(() => {
this.$router.go(-1)
}, 200)
} catch (e) {
this.submitLoading = false
console.log(e)
}
},
navigateBack() {
this.isLeave = true
if (this.$parent && typeof this.$parent.onActivated === 'function') {
this.$parent.onActivated()
}
setTimeout(() => {
this.$router.go(-1)
}, 200)
},
onTranslateDone() {
this.navigateBack()
},
goBackTranslateList() {
this.navigateBack()
},
onApplyConfirm() {
this.applyDialog = true
},