401 lines
9.8 KiB
Vue
401 lines
9.8 KiB
Vue
<script lang="ts" setup>
|
|
import { computed, reactive, ref, watch } from 'vue';
|
|
|
|
import {
|
|
OSS_FILE_BUCKETS,
|
|
getAccessImgUrl,
|
|
simpleUploadFile,
|
|
} from '#/api/legacy/oss';
|
|
import {
|
|
addPropsSource,
|
|
updatePropsSource,
|
|
} from '#/api/legacy/props';
|
|
|
|
import {
|
|
Button,
|
|
Form,
|
|
FormItem,
|
|
Image,
|
|
Input,
|
|
Modal,
|
|
Select,
|
|
Switch,
|
|
message,
|
|
} from 'antdv-next';
|
|
|
|
import {
|
|
NOBLE_VIP_OPTIONS,
|
|
PROPS_TYPES,
|
|
} from '../shared';
|
|
|
|
const props = defineProps<{
|
|
open: boolean;
|
|
record: null | Record<string, any>;
|
|
sysOriginOptions: Array<{ label: string; value: string }>;
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
close: [];
|
|
success: [];
|
|
}>();
|
|
|
|
const saving = ref(false);
|
|
const coverUploading = ref(false);
|
|
const sourceUploading = ref(false);
|
|
const expandUploading = ref(false);
|
|
|
|
const coverInputRef = ref<HTMLInputElement>();
|
|
const sourceInputRef = ref<HTMLInputElement>();
|
|
const expandInputRef = ref<HTMLInputElement>();
|
|
|
|
const form = reactive<Record<string, any>>({
|
|
adminFree: false,
|
|
amount: '',
|
|
code: '',
|
|
cover: '',
|
|
expand: '',
|
|
id: '',
|
|
name: '',
|
|
sourceUrl: '',
|
|
sysOrigin: '',
|
|
type: '',
|
|
});
|
|
|
|
const isUpdate = computed(() => Boolean(form.id));
|
|
const isImageSourceType = computed(() =>
|
|
['THEME', 'LAYOUT', 'CHAT_BUBBLE'].includes(form.type),
|
|
);
|
|
const isAmountRequired = computed(
|
|
() => !['CUSTOMIZE', 'FRAGMENTS'].includes(form.type),
|
|
);
|
|
const isSourceRequired = computed(
|
|
() => !['CUSTOMIZE', 'FRAGMENTS'].includes(form.type),
|
|
);
|
|
const showExpandUpload = computed(() => form.type === 'CHAT_BUBBLE');
|
|
const title = computed(() => (isUpdate.value ? '修改资源' : '新增资源'));
|
|
const propsTypeOptions = PROPS_TYPES.map((item) => ({
|
|
label: item.name,
|
|
value: item.value as any,
|
|
}));
|
|
const nobleVipNameOptions = NOBLE_VIP_OPTIONS.map((item) => ({
|
|
label: item.name,
|
|
value: item.value as any,
|
|
}));
|
|
|
|
function resetForm() {
|
|
form.adminFree = false;
|
|
form.amount = '';
|
|
form.code = '';
|
|
form.cover = '';
|
|
form.expand = '';
|
|
form.id = '';
|
|
form.name = '';
|
|
form.sourceUrl = '';
|
|
form.sysOrigin = props.sysOriginOptions[0]?.value ?? 'LIKEI';
|
|
form.type = PROPS_TYPES[0]?.value ?? '';
|
|
}
|
|
|
|
watch(
|
|
() => props.open,
|
|
(open) => {
|
|
if (!open) {
|
|
return;
|
|
}
|
|
const record = props.record;
|
|
if (!record) {
|
|
resetForm();
|
|
return;
|
|
}
|
|
form.adminFree = Boolean(record.adminFree);
|
|
form.amount = record.amount ?? '';
|
|
form.code = record.code ?? '';
|
|
form.cover = record.cover ?? '';
|
|
form.expand = record.expand ?? '';
|
|
form.id = record.id ?? '';
|
|
form.name = record.name ?? '';
|
|
form.sourceUrl = record.sourceUrl ?? '';
|
|
form.sysOrigin = record.sysOrigin || props.sysOriginOptions[0]?.value || 'LIKEI';
|
|
form.type = record.type ?? '';
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
|
|
watch(
|
|
() => form.type,
|
|
(value) => {
|
|
if (!value) {
|
|
return;
|
|
}
|
|
if (value === 'NOBLE_VIP' && !NOBLE_VIP_OPTIONS.some((item) => item.value === form.name)) {
|
|
form.name = '';
|
|
}
|
|
if (['CUSTOMIZE', 'FRAGMENTS'].includes(value)) {
|
|
form.amount = 0;
|
|
form.sourceUrl = '';
|
|
form.expand = '';
|
|
}
|
|
if (value !== 'CHAT_BUBBLE') {
|
|
form.expand = '';
|
|
}
|
|
},
|
|
);
|
|
|
|
function openCoverPicker() {
|
|
coverInputRef.value?.click();
|
|
}
|
|
|
|
function openSourcePicker() {
|
|
sourceInputRef.value?.click();
|
|
}
|
|
|
|
function openExpandPicker() {
|
|
expandInputRef.value?.click();
|
|
}
|
|
|
|
function handleCoverInput(event: Event) {
|
|
void uploadFile(event, 'cover', coverUploading, true);
|
|
}
|
|
|
|
function handleSourceInput(event: Event) {
|
|
void uploadFile(event, 'sourceUrl', sourceUploading, isImageSourceType.value);
|
|
}
|
|
|
|
function handleExpandInput(event: Event) {
|
|
void uploadFile(event, 'expand', expandUploading, true);
|
|
}
|
|
|
|
async function uploadFile(
|
|
event: Event,
|
|
targetKey: 'cover' | 'expand' | 'sourceUrl',
|
|
loadingRef: typeof coverUploading,
|
|
imageBucket = false,
|
|
) {
|
|
const target = event.target as HTMLInputElement;
|
|
const file = target.files?.[0];
|
|
target.value = '';
|
|
if (!file) {
|
|
return;
|
|
}
|
|
loadingRef.value = true;
|
|
try {
|
|
const result = await simpleUploadFile(
|
|
file,
|
|
imageBucket ? OSS_FILE_BUCKETS.svgaCover : OSS_FILE_BUCKETS.svgasource,
|
|
);
|
|
form[targetKey] = getAccessImgUrl(result.name);
|
|
message.success('上传成功');
|
|
} finally {
|
|
loadingRef.value = false;
|
|
}
|
|
}
|
|
|
|
function validateForm() {
|
|
if (!form.sysOrigin) {
|
|
message.warning('请选择系统');
|
|
return false;
|
|
}
|
|
if (!form.type) {
|
|
message.warning('请选择类型');
|
|
return false;
|
|
}
|
|
if (!String(form.cover || '').trim()) {
|
|
message.warning('请上传封面');
|
|
return false;
|
|
}
|
|
if (isSourceRequired.value && !String(form.sourceUrl || '').trim()) {
|
|
message.warning('请上传资源');
|
|
return false;
|
|
}
|
|
if (showExpandUpload.value && !String(form.expand || '').trim()) {
|
|
message.warning('请上传 Android 资源');
|
|
return false;
|
|
}
|
|
if (!String(form.name || '').trim()) {
|
|
message.warning(form.type === 'NOBLE_VIP' ? '请选择贵族类型' : '请输入名称');
|
|
return false;
|
|
}
|
|
if (!isUpdate.value && !String(form.code || '').trim()) {
|
|
message.warning('请输入编码');
|
|
return false;
|
|
}
|
|
if (isAmountRequired.value && (form.amount === '' || Number.isNaN(Number(form.amount)))) {
|
|
message.warning('请输入底价');
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
async function handleSubmit() {
|
|
if (!validateForm()) {
|
|
return;
|
|
}
|
|
saving.value = true;
|
|
try {
|
|
const payload = {
|
|
...form,
|
|
amount: isAmountRequired.value ? Number(form.amount) : 0,
|
|
};
|
|
if (isUpdate.value) {
|
|
await updatePropsSource(payload);
|
|
} else {
|
|
await addPropsSource(payload);
|
|
}
|
|
message.success('保存成功');
|
|
emit('success');
|
|
emit('close');
|
|
} finally {
|
|
saving.value = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<Modal
|
|
:confirm-loading="saving"
|
|
:open="open"
|
|
destroy-on-close
|
|
:title="title"
|
|
width="720"
|
|
@cancel="emit('close')"
|
|
@ok="handleSubmit"
|
|
>
|
|
<Form layout="vertical">
|
|
<FormItem label="系统">
|
|
<SysOriginSelect v-model:value="form.sysOrigin"
|
|
:options="sysOriginOptions"
|
|
></SysOriginSelect>
|
|
</FormItem>
|
|
<FormItem label="类型">
|
|
<Select
|
|
option-label-prop="label"
|
|
v-model:value="form.type"
|
|
:options="propsTypeOptions"
|
|
/>
|
|
</FormItem>
|
|
<FormItem label="封面">
|
|
<input
|
|
ref="coverInputRef"
|
|
accept="image/*"
|
|
class="hidden-input"
|
|
type="file"
|
|
@change="handleCoverInput"
|
|
/>
|
|
<div class="upload-field">
|
|
<div v-if="form.cover" class="upload-preview">
|
|
<Image class="upload-preview__image" :src="form.cover" />
|
|
</div>
|
|
<div v-else class="upload-empty">请上传封面</div>
|
|
<Button :loading="coverUploading" @click="openCoverPicker">
|
|
{{ form.cover ? '重新上传' : '上传封面' }}
|
|
</Button>
|
|
</div>
|
|
</FormItem>
|
|
<FormItem v-if="isSourceRequired" :label="showExpandUpload ? 'iOS资源' : '资源'">
|
|
<input
|
|
ref="sourceInputRef"
|
|
:accept="isImageSourceType ? 'image/*' : '.svga,.pag,.mp4'"
|
|
class="hidden-input"
|
|
type="file"
|
|
@change="handleSourceInput"
|
|
/>
|
|
<div class="upload-field">
|
|
<div v-if="form.sourceUrl" class="upload-preview">
|
|
<Image
|
|
v-if="isImageSourceType"
|
|
class="upload-preview__image"
|
|
:src="form.sourceUrl"
|
|
/>
|
|
<div v-else class="upload-file">
|
|
{{ form.sourceUrl }}
|
|
</div>
|
|
</div>
|
|
<div v-else class="upload-empty">请上传资源</div>
|
|
<Button :loading="sourceUploading" @click="openSourcePicker">
|
|
{{ form.sourceUrl ? '重新上传' : '上传资源' }}
|
|
</Button>
|
|
</div>
|
|
</FormItem>
|
|
<FormItem v-if="showExpandUpload" label="Android资源">
|
|
<input
|
|
ref="expandInputRef"
|
|
accept="image/*"
|
|
class="hidden-input"
|
|
type="file"
|
|
@change="handleExpandInput"
|
|
/>
|
|
<div class="upload-field">
|
|
<div v-if="form.expand" class="upload-preview">
|
|
<Image class="upload-preview__image" :src="form.expand" />
|
|
</div>
|
|
<div v-else class="upload-empty">请上传 Android 资源</div>
|
|
<Button :loading="expandUploading" @click="openExpandPicker">
|
|
{{ form.expand ? '重新上传' : '上传资源' }}
|
|
</Button>
|
|
</div>
|
|
</FormItem>
|
|
<FormItem label="名称">
|
|
<Select
|
|
v-if="form.type === 'NOBLE_VIP'"
|
|
option-label-prop="label"
|
|
v-model:value="form.name"
|
|
:options="nobleVipNameOptions"
|
|
/>
|
|
<Input v-else v-model:value="form.name" />
|
|
</FormItem>
|
|
<FormItem v-if="!isUpdate" label="编码">
|
|
<Input v-model:value="form.code" placeholder="编码添加后不可修改" />
|
|
</FormItem>
|
|
<FormItem v-if="isAmountRequired" label="底价">
|
|
<Input v-model:value="form.amount" />
|
|
</FormItem>
|
|
<FormItem label="Admin Free">
|
|
<Switch
|
|
v-model:checked="form.adminFree"
|
|
checked-children="是"
|
|
un-checked-children="否"
|
|
/>
|
|
</FormItem>
|
|
</Form>
|
|
</Modal>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.hidden-input {
|
|
display: none;
|
|
}
|
|
|
|
.upload-field {
|
|
display: grid;
|
|
gap: 12px;
|
|
}
|
|
|
|
.upload-preview,
|
|
.upload-empty {
|
|
align-items: center;
|
|
background: rgb(248 250 252);
|
|
border: 1px dashed rgb(148 163 184 / 35%);
|
|
border-radius: 16px;
|
|
display: flex;
|
|
justify-content: center;
|
|
min-height: 180px;
|
|
overflow: hidden;
|
|
padding: 12px;
|
|
}
|
|
|
|
.upload-empty {
|
|
color: rgb(148 163 184);
|
|
}
|
|
|
|
.upload-preview__image {
|
|
height: 180px;
|
|
object-fit: contain;
|
|
width: 100%;
|
|
}
|
|
|
|
.upload-file {
|
|
color: rgb(51 65 85);
|
|
line-height: 1.5;
|
|
word-break: break-all;
|
|
}
|
|
</style>
|