834 lines
20 KiB
Vue
834 lines
20 KiB
Vue
<script lang="ts" setup>
|
|
import { computed, reactive, ref, watch } from 'vue';
|
|
|
|
import {
|
|
Button,
|
|
Form,
|
|
FormItem,
|
|
Image,
|
|
Input,
|
|
InputNumber,
|
|
message,
|
|
Select,
|
|
Switch,
|
|
} from 'antdv-next';
|
|
|
|
import {
|
|
getAccessImgUrl,
|
|
OSS_FILE_BUCKETS,
|
|
simpleUploadFile,
|
|
} from '#/api/legacy/oss';
|
|
import {
|
|
addPropsSource,
|
|
updatePropsSource,
|
|
} from '#/api/legacy/props';
|
|
|
|
import {
|
|
BADGE_DISPLAY_TYPE_OPTIONS,
|
|
NOBLE_VIP_OPTIONS,
|
|
PROPS_RESOURCE_CONFIG_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 notSelectUploading = ref(false);
|
|
|
|
const coverInputRef = ref<HTMLInputElement>();
|
|
const sourceInputRef = ref<HTMLInputElement>();
|
|
const expandInputRef = ref<HTMLInputElement>();
|
|
const notSelectInputRef = ref<HTMLInputElement>();
|
|
|
|
const form = reactive<Record<string, any>>({
|
|
adminFree: false,
|
|
amount: '',
|
|
badgeDisplayType: 'SHORT',
|
|
code: '',
|
|
cover: '',
|
|
expand: '',
|
|
id: '',
|
|
name: '',
|
|
notSelectUrl: '',
|
|
sourceUrl: '',
|
|
sysOrigin: '',
|
|
type: '',
|
|
});
|
|
|
|
const isUpdate = computed(() => Boolean(form.id));
|
|
const isImageSourceType = computed(() =>
|
|
['CHAT_BUBBLE', 'LAYOUT', 'THEME'].includes(form.type),
|
|
);
|
|
const isAmountRequired = computed(
|
|
() => !['CUSTOMIZE', 'FRAGMENTS'].includes(form.type),
|
|
);
|
|
const showSourceUpload = computed(
|
|
() => !['CUSTOMIZE', 'FRAGMENTS'].includes(form.type),
|
|
);
|
|
const isSourceRequired = computed(
|
|
() => !['BADGE', 'CUSTOMIZE', 'FRAGMENTS'].includes(form.type),
|
|
);
|
|
const showExpandUpload = computed(() => form.type === 'CHAT_BUBBLE');
|
|
const sourceUploadLabel = computed(() => {
|
|
if (form.type === 'BADGE') {
|
|
return '动效资源(选填)';
|
|
}
|
|
if (showExpandUpload.value) {
|
|
return 'iOS资源';
|
|
}
|
|
return isSourceRequired.value ? '资源' : '资源(选填)';
|
|
});
|
|
const title = computed(() => (isUpdate.value ? '修改资源' : '新增资源'));
|
|
const propsTypeOptions = PROPS_RESOURCE_CONFIG_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,
|
|
}));
|
|
const badgeDisplayTypeOptions = BADGE_DISPLAY_TYPE_OPTIONS.map((item) => ({
|
|
label: item.name,
|
|
value: item.value as any,
|
|
}));
|
|
|
|
function parseBadgeExpand(value: unknown) {
|
|
if (!value || typeof value !== 'string') {
|
|
return {};
|
|
}
|
|
try {
|
|
return JSON.parse(value) as Record<string, any>;
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
function applyBadgeExpand(value: unknown) {
|
|
const expand = parseBadgeExpand(value);
|
|
form.badgeDisplayType = normalizeBadgeDisplayType(
|
|
expand.badgeDisplayType || expand.displayType,
|
|
expand.badgeType,
|
|
);
|
|
form.notSelectUrl = expand.notSelectUrl ?? '';
|
|
}
|
|
|
|
function buildBadgeExpand() {
|
|
const expand: Record<string, any> = {
|
|
badgeDisplayType: normalizeBadgeDisplayType(form.badgeDisplayType),
|
|
};
|
|
if (String(form.notSelectUrl || '').trim()) {
|
|
expand.notSelectUrl = String(form.notSelectUrl).trim();
|
|
}
|
|
return JSON.stringify(expand);
|
|
}
|
|
|
|
function normalizeBadgeDisplayType(value: unknown, badgeType?: unknown) {
|
|
const displayType = String(value || '').trim().toUpperCase();
|
|
if (displayType === 'LONG' || displayType === 'SHORT') {
|
|
return displayType;
|
|
}
|
|
return String(badgeType || '').trim().toUpperCase() === 'VIP' ? 'LONG' : 'SHORT';
|
|
}
|
|
|
|
function resetForm() {
|
|
form.adminFree = false;
|
|
form.amount = '';
|
|
form.badgeDisplayType = 'SHORT';
|
|
form.code = '';
|
|
form.cover = '';
|
|
form.expand = '';
|
|
form.id = '';
|
|
form.name = '';
|
|
form.notSelectUrl = '';
|
|
form.sourceUrl = '';
|
|
form.sysOrigin = props.sysOriginOptions[0]?.value ?? 'LIKEI';
|
|
form.type = PROPS_RESOURCE_CONFIG_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 ?? '';
|
|
if (form.type === 'BADGE') {
|
|
applyBadgeExpand(record.expand);
|
|
} else {
|
|
applyBadgeExpand('');
|
|
}
|
|
},
|
|
{ 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 === 'BADGE' && !form.badgeDisplayType) {
|
|
form.badgeDisplayType = 'SHORT';
|
|
}
|
|
if (!['BADGE', 'CHAT_BUBBLE'].includes(value)) {
|
|
form.expand = '';
|
|
}
|
|
},
|
|
);
|
|
|
|
function openCoverPicker() {
|
|
coverInputRef.value?.click();
|
|
}
|
|
|
|
function openSourcePicker() {
|
|
sourceInputRef.value?.click();
|
|
}
|
|
|
|
function openExpandPicker() {
|
|
expandInputRef.value?.click();
|
|
}
|
|
|
|
function openNotSelectPicker() {
|
|
notSelectInputRef.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);
|
|
}
|
|
|
|
function handleNotSelectInput(event: Event) {
|
|
void uploadFile(event, 'notSelectUrl', notSelectUploading, true);
|
|
}
|
|
|
|
function clearUpload(targetKey: 'cover' | 'expand' | 'notSelectUrl' | 'sourceUrl') {
|
|
form[targetKey] = '';
|
|
}
|
|
|
|
function getAssetName(value: unknown) {
|
|
const text = String(value || '').trim();
|
|
if (!text) {
|
|
return '';
|
|
}
|
|
return decodeURIComponent(text.split('/').pop() || text);
|
|
}
|
|
|
|
async function uploadFile(
|
|
event: Event,
|
|
targetKey: 'cover' | 'expand' | 'notSelectUrl' | '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 (form.type === 'BADGE' && !form.badgeDisplayType) {
|
|
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 === '' ||
|
|
form.amount === null ||
|
|
form.amount === undefined ||
|
|
Number.isNaN(Number(form.amount)))
|
|
) {
|
|
message.warning('请输入底价');
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
async function handleSubmit() {
|
|
if (!validateForm()) {
|
|
return;
|
|
}
|
|
saving.value = true;
|
|
try {
|
|
const payload = {
|
|
adminFree: form.adminFree,
|
|
amount: isAmountRequired.value ? Number(form.amount) : 0,
|
|
code: form.code,
|
|
cover: form.cover,
|
|
expand: form.type === 'BADGE' ? buildBadgeExpand() : form.expand,
|
|
id: form.id,
|
|
name: form.name,
|
|
sourceUrl: form.sourceUrl,
|
|
sysOrigin: form.sysOrigin,
|
|
type: form.type,
|
|
};
|
|
await (isUpdate.value ? updatePropsSource(payload) : addPropsSource(payload));
|
|
message.success('保存成功');
|
|
emit('success');
|
|
emit('close');
|
|
} finally {
|
|
saving.value = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<section v-if="open" class="quick-editor">
|
|
<input
|
|
ref="coverInputRef"
|
|
accept="image/*"
|
|
class="hidden-input"
|
|
type="file"
|
|
@change="handleCoverInput"
|
|
/>
|
|
<input
|
|
ref="notSelectInputRef"
|
|
accept="image/*"
|
|
class="hidden-input"
|
|
type="file"
|
|
@change="handleNotSelectInput"
|
|
/>
|
|
<input
|
|
ref="sourceInputRef"
|
|
:accept="isImageSourceType ? 'image/*' : '.svga,.pag,.mp4'"
|
|
class="hidden-input"
|
|
type="file"
|
|
@change="handleSourceInput"
|
|
/>
|
|
<input
|
|
ref="expandInputRef"
|
|
accept="image/*"
|
|
class="hidden-input"
|
|
type="file"
|
|
@change="handleExpandInput"
|
|
/>
|
|
|
|
<div class="quick-editor__bar">
|
|
<div class="quick-editor__title">{{ title }}</div>
|
|
<div class="quick-editor__actions">
|
|
<Button @click="emit('close')">取消</Button>
|
|
<Button :loading="saving" type="primary" @click="handleSubmit">
|
|
保存
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<Form class="resource-form" layout="vertical">
|
|
<section class="compact-section">
|
|
<div class="section-title">基础配置</div>
|
|
<div class="form-grid">
|
|
<FormItem label="系统">
|
|
<SysOriginSelect
|
|
v-model:value="form.sysOrigin"
|
|
:options="sysOriginOptions"
|
|
/>
|
|
</FormItem>
|
|
<FormItem label="类型">
|
|
<Select
|
|
v-model:value="form.type"
|
|
option-label-prop="label"
|
|
:options="propsTypeOptions"
|
|
/>
|
|
</FormItem>
|
|
<FormItem v-if="form.type === 'BADGE'" label="展示形态">
|
|
<Select
|
|
v-model:value="form.badgeDisplayType"
|
|
option-label-prop="label"
|
|
:options="badgeDisplayTypeOptions"
|
|
/>
|
|
</FormItem>
|
|
<FormItem v-if="isAmountRequired" label="底价">
|
|
<InputNumber
|
|
v-model:value="form.amount"
|
|
:min="0"
|
|
placeholder="请输入底价"
|
|
style="width: 100%"
|
|
/>
|
|
</FormItem>
|
|
<FormItem label="Admin Free">
|
|
<div class="switch-line">
|
|
<Switch
|
|
v-model:checked="form.adminFree"
|
|
checked-children="是"
|
|
un-checked-children="否"
|
|
/>
|
|
</div>
|
|
</FormItem>
|
|
<FormItem class="form-grid__wide" label="名称">
|
|
<Select
|
|
v-if="form.type === 'NOBLE_VIP'"
|
|
v-model:value="form.name"
|
|
option-label-prop="label"
|
|
:options="nobleVipNameOptions"
|
|
/>
|
|
<Input v-else v-model:value="form.name" />
|
|
</FormItem>
|
|
<FormItem v-if="!isUpdate" label="编码">
|
|
<Input v-model:value="form.code" placeholder="添加后不可修改" />
|
|
</FormItem>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="compact-section">
|
|
<div class="section-title">资源文件</div>
|
|
<div class="asset-grid">
|
|
<div class="asset-card">
|
|
<div class="asset-preview">
|
|
<Image
|
|
v-if="form.cover"
|
|
class="asset-preview__image"
|
|
:src="form.cover"
|
|
/>
|
|
<div v-else class="asset-empty">未上传</div>
|
|
</div>
|
|
<div class="asset-detail">
|
|
<div class="asset-card__head">
|
|
<span>封面</span>
|
|
<span
|
|
class="asset-status"
|
|
:class="{ 'asset-status--ready': form.cover }"
|
|
>
|
|
{{ form.cover ? '已上传' : '必填' }}
|
|
</span>
|
|
</div>
|
|
<div class="asset-meta">{{ getAssetName(form.cover) || '-' }}</div>
|
|
</div>
|
|
<div class="asset-actions">
|
|
<Button :loading="coverUploading" @click="openCoverPicker">
|
|
{{ form.cover ? '替换' : '上传' }}
|
|
</Button>
|
|
<Button
|
|
v-if="form.cover"
|
|
danger
|
|
type="text"
|
|
@click="clearUpload('cover')"
|
|
>
|
|
删除
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="showSourceUpload" class="asset-card">
|
|
<div class="asset-preview">
|
|
<Image
|
|
v-if="form.sourceUrl && isImageSourceType"
|
|
class="asset-preview__image"
|
|
:src="form.sourceUrl"
|
|
/>
|
|
<div v-else-if="form.sourceUrl" class="asset-file">
|
|
{{ getAssetName(form.sourceUrl) }}
|
|
</div>
|
|
<div v-else class="asset-empty">未上传</div>
|
|
</div>
|
|
<div class="asset-detail">
|
|
<div class="asset-card__head">
|
|
<span>{{ sourceUploadLabel }}</span>
|
|
<span
|
|
class="asset-status"
|
|
:class="{ 'asset-status--ready': form.sourceUrl }"
|
|
>
|
|
{{ form.sourceUrl ? '已上传' : isSourceRequired ? '必填' : '选填' }}
|
|
</span>
|
|
</div>
|
|
<div class="asset-meta">
|
|
{{ getAssetName(form.sourceUrl) || '-' }}
|
|
</div>
|
|
</div>
|
|
<div class="asset-actions">
|
|
<Button :loading="sourceUploading" @click="openSourcePicker">
|
|
{{ form.sourceUrl ? '替换' : '上传' }}
|
|
</Button>
|
|
<Button
|
|
v-if="form.sourceUrl"
|
|
danger
|
|
type="text"
|
|
@click="clearUpload('sourceUrl')"
|
|
>
|
|
删除
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="showExpandUpload" class="asset-card">
|
|
<div class="asset-preview">
|
|
<Image
|
|
v-if="form.expand"
|
|
class="asset-preview__image"
|
|
:src="form.expand"
|
|
/>
|
|
<div v-else class="asset-empty">未上传</div>
|
|
</div>
|
|
<div class="asset-detail">
|
|
<div class="asset-card__head">
|
|
<span>Android资源</span>
|
|
<span
|
|
class="asset-status"
|
|
:class="{ 'asset-status--ready': form.expand }"
|
|
>
|
|
{{ form.expand ? '已上传' : '必填' }}
|
|
</span>
|
|
</div>
|
|
<div class="asset-meta">{{ getAssetName(form.expand) || '-' }}</div>
|
|
</div>
|
|
<div class="asset-actions">
|
|
<Button :loading="expandUploading" @click="openExpandPicker">
|
|
{{ form.expand ? '替换' : '上传' }}
|
|
</Button>
|
|
<Button
|
|
v-if="form.expand"
|
|
danger
|
|
type="text"
|
|
@click="clearUpload('expand')"
|
|
>
|
|
删除
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="form.type === 'BADGE'" class="asset-card">
|
|
<div class="asset-preview">
|
|
<Image
|
|
v-if="form.notSelectUrl"
|
|
class="asset-preview__image"
|
|
:src="form.notSelectUrl"
|
|
/>
|
|
<div v-else class="asset-empty">未上传</div>
|
|
</div>
|
|
<div class="asset-detail">
|
|
<div class="asset-card__head">
|
|
<span>未选中图</span>
|
|
<span
|
|
class="asset-status"
|
|
:class="{ 'asset-status--ready': form.notSelectUrl }"
|
|
>
|
|
{{ form.notSelectUrl ? '已上传' : '选填' }}
|
|
</span>
|
|
</div>
|
|
<div class="asset-meta">
|
|
{{ getAssetName(form.notSelectUrl) || '-' }}
|
|
</div>
|
|
</div>
|
|
<div class="asset-actions">
|
|
<Button :loading="notSelectUploading" @click="openNotSelectPicker">
|
|
{{ form.notSelectUrl ? '替换' : '上传' }}
|
|
</Button>
|
|
<Button
|
|
v-if="form.notSelectUrl"
|
|
danger
|
|
type="text"
|
|
@click="clearUpload('notSelectUrl')"
|
|
>
|
|
删除
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</Form>
|
|
</section>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.hidden-input {
|
|
display: none;
|
|
}
|
|
|
|
.quick-editor {
|
|
background: #fff;
|
|
border: 1px solid #d8dee8;
|
|
border-radius: 8px;
|
|
box-shadow: 0 1px 2px rgb(16 24 40 / 4%);
|
|
margin-bottom: 8px;
|
|
padding: 12px;
|
|
}
|
|
|
|
.quick-editor__bar {
|
|
align-items: center;
|
|
border-bottom: 1px solid #eef0f3;
|
|
display: flex;
|
|
gap: 16px;
|
|
justify-content: space-between;
|
|
margin-bottom: 12px;
|
|
padding-bottom: 10px;
|
|
}
|
|
|
|
.quick-editor__title {
|
|
color: #111827;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
line-height: 24px;
|
|
}
|
|
|
|
.quick-editor__actions {
|
|
align-items: center;
|
|
display: flex;
|
|
flex: 0 0 auto;
|
|
gap: 8px;
|
|
}
|
|
|
|
.section-title {
|
|
color: #111827;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
line-height: 20px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.resource-form {
|
|
display: grid;
|
|
gap: 12px;
|
|
}
|
|
|
|
.resource-form :deep(.ant-form-item) {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.resource-form :deep(.ant-form-item-label) {
|
|
padding-bottom: 3px;
|
|
}
|
|
|
|
.resource-form :deep(.ant-form-item-label > label) {
|
|
color: #475467;
|
|
font-size: 13px;
|
|
height: 18px;
|
|
}
|
|
|
|
.compact-section {
|
|
border-bottom: 1px solid #eef0f3;
|
|
padding-bottom: 12px;
|
|
}
|
|
|
|
.compact-section:last-child {
|
|
border-bottom: 0;
|
|
padding-bottom: 0;
|
|
}
|
|
|
|
.form-grid {
|
|
display: grid;
|
|
gap: 12px;
|
|
grid-template-columns: repeat(4, minmax(170px, 1fr));
|
|
}
|
|
|
|
.form-grid__wide {
|
|
grid-column: span 2;
|
|
}
|
|
|
|
.switch-line {
|
|
align-items: center;
|
|
display: flex;
|
|
gap: 8px;
|
|
min-height: 32px;
|
|
}
|
|
|
|
.asset-grid {
|
|
display: grid;
|
|
gap: 10px;
|
|
grid-template-columns: repeat(2, minmax(320px, 1fr));
|
|
}
|
|
|
|
.asset-card {
|
|
align-items: center;
|
|
background: #fbfdff;
|
|
border: 1px solid #e5e7eb;
|
|
border-radius: 6px;
|
|
display: grid;
|
|
gap: 12px;
|
|
grid-template-columns: 96px minmax(0, 1fr) auto;
|
|
min-width: 0;
|
|
padding: 12px;
|
|
}
|
|
|
|
.asset-card__head,
|
|
.asset-actions {
|
|
align-items: center;
|
|
display: flex;
|
|
}
|
|
|
|
.asset-card__head {
|
|
color: #344054;
|
|
gap: 8px;
|
|
font-weight: 600;
|
|
min-width: 0;
|
|
}
|
|
|
|
.asset-card__head span:first-child {
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.asset-status {
|
|
background: #f3f4f6;
|
|
border-radius: 999px;
|
|
color: #667085;
|
|
flex: 0 0 auto;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
line-height: 20px;
|
|
padding: 0 8px;
|
|
}
|
|
|
|
.asset-status--ready {
|
|
background: #e8f3ff;
|
|
color: #1677ff;
|
|
}
|
|
|
|
.asset-preview {
|
|
align-items: center;
|
|
background: #f8fafc;
|
|
border: 1px dashed #cbd5e1;
|
|
border-radius: 6px;
|
|
display: flex;
|
|
height: 76px;
|
|
justify-content: center;
|
|
min-height: 0;
|
|
overflow: hidden;
|
|
padding: 6px;
|
|
}
|
|
|
|
.asset-preview__image {
|
|
height: 64px;
|
|
object-fit: contain;
|
|
width: 100%;
|
|
}
|
|
|
|
.asset-empty {
|
|
color: #98a2b3;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.asset-file {
|
|
color: #344054;
|
|
font-size: 12px;
|
|
line-height: 1.35;
|
|
max-width: 100%;
|
|
overflow-wrap: anywhere;
|
|
text-align: center;
|
|
}
|
|
|
|
.asset-meta {
|
|
color: #667085;
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.asset-actions {
|
|
gap: 6px;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
@media (max-width: 1120px) {
|
|
.form-grid {
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
}
|
|
|
|
.form-grid__wide {
|
|
grid-column: span 2;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 720px) {
|
|
.asset-grid,
|
|
.form-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.form-grid__wide {
|
|
grid-column: span 1;
|
|
}
|
|
|
|
.asset-card {
|
|
grid-template-columns: 80px minmax(0, 1fr);
|
|
}
|
|
|
|
.asset-actions {
|
|
grid-column: 1 / -1;
|
|
justify-content: flex-start;
|
|
}
|
|
}
|
|
</style>
|