feat: 添加媒体资产文件名支持,优化上传和内容获取逻辑

This commit is contained in:
2026-01-04 19:09:34 +08:00
parent 5496c776df
commit df08e148ce
3 changed files with 139 additions and 39 deletions

View File

@@ -545,15 +545,38 @@ func (s *creator) GetSettings(ctx context.Context, userID int64) (*creator_dto.S
if err != nil { if err != nil {
return nil, errorx.ErrRecordNotFound return nil, errorx.ErrRecordNotFound
} }
// Extract from t.Config cfg := t.Config.Data()
return &creator_dto.Settings{ return &creator_dto.Settings{
Name: t.Name, Name: t.Name,
// Bio/Avatar from Config Bio: cfg.Bio,
Avatar: cfg.Avatar,
Cover: cfg.Cover,
Description: cfg.Description,
}, nil }, nil
} }
func (s *creator) UpdateSettings(ctx context.Context, userID int64, form *creator_dto.Settings) error { func (s *creator) UpdateSettings(ctx context.Context, userID int64, form *creator_dto.Settings) error {
return nil tid, err := s.getTenantID(ctx, userID)
if err != nil {
return err
}
t, err := models.TenantQuery.WithContext(ctx).Where(models.TenantQuery.ID.Eq(tid)).First()
if err != nil {
return errorx.ErrRecordNotFound
}
cfg := t.Config.Data()
cfg.Bio = form.Bio
cfg.Avatar = form.Avatar
cfg.Cover = form.Cover
cfg.Description = form.Description
_, err = models.TenantQuery.WithContext(ctx).Where(models.TenantQuery.ID.Eq(tid)).Updates(&models.Tenant{
Name: form.Name,
Config: types.NewJSONType(cfg),
})
return err
} }
func (s *creator) ListPayoutAccounts(ctx context.Context, userID int64) ([]creator_dto.PayoutAccount, error) { func (s *creator) ListPayoutAccounts(ctx context.Context, userID int64) ([]creator_dto.PayoutAccount, error) {

View File

@@ -2,6 +2,10 @@ package fields
// TenantConfig 租户配置 // TenantConfig 租户配置
type TenantConfig struct { type TenantConfig struct {
Theme string `json:"theme,omitempty"` Theme string `json:"theme,omitempty"`
Features []string `json:"features,omitempty"` Features []string `json:"features,omitempty"`
Bio string `json:"bio,omitempty"`
Avatar string `json:"avatar,omitempty"`
Cover string `json:"cover,omitempty"`
Description string `json:"description,omitempty"`
} }

View File

@@ -98,19 +98,26 @@
</div> </div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Mock Account Card --> <div v-for="acc in payoutAccounts" :key="acc.id"
<div
class="p-4 border border-slate-200 rounded-xl flex items-center gap-4 bg-white relative group hover:border-primary-200 transition-colors"> class="p-4 border border-slate-200 rounded-xl flex items-center gap-4 bg-white relative group hover:border-primary-200 transition-colors">
<div class="w-10 h-10 rounded-full bg-red-50 text-red-600 flex items-center justify-center"><i <div class="w-10 h-10 rounded-full flex items-center justify-center text-xl"
class="pi pi-briefcase"></i></div> :class="acc.type === 'alipay' ? 'bg-blue-50 text-blue-600' : 'bg-red-50 text-red-600'">
<div> <i class="pi" :class="acc.type === 'alipay' ? 'pi-mobile' : 'pi-briefcase'"></i>
<div class="font-bold text-slate-900">招商银行</div>
<div class="text-sm text-slate-500">储蓄卡 (8888)</div>
</div> </div>
<button <div>
<div class="font-bold text-slate-900">{{ acc.name }}</div>
<div class="text-sm text-slate-500">{{ acc.type === 'alipay' ? '支付宝' : '银行卡' }} ({{ acc.account.slice(-4) }})</div>
</div>
<button @click="removeAccount(acc.id)"
class="absolute top-4 right-4 text-slate-300 hover:text-red-500 cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity p-2"><i class="absolute top-4 right-4 text-slate-300 hover:text-red-500 cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity p-2"><i
class="pi pi-trash"></i></button> class="pi pi-trash"></i></button>
</div> </div>
<!-- Empty State -->
<div v-if="payoutAccounts.length === 0" class="col-span-full py-8 text-center text-slate-400 bg-slate-50 rounded-xl border border-dashed border-slate-200">
<i class="pi pi-wallet text-2xl mb-2"></i>
<p>暂无收款账户请添加</p>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -175,12 +182,15 @@ import Dialog from 'primevue/dialog';
import RadioButton from 'primevue/radiobutton'; import RadioButton from 'primevue/radiobutton';
import Toast from 'primevue/toast'; import Toast from 'primevue/toast';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import { reactive, ref } from 'vue'; import { reactive, ref, onMounted } from 'vue';
import { creatorApi } from '../../api/creator';
import { commonApi } from '../../api/common';
const toast = useToast(); const toast = useToast();
const fileInput = ref(null); const fileInput = ref(null);
const currentUploadType = ref(''); const currentUploadType = ref('');
const showAddAccount = ref(false); const showAddAccount = ref(false);
const payoutAccounts = ref([]);
const newAccount = reactive({ const newAccount = reactive({
type: 'bank', type: 'bank',
@@ -190,43 +200,106 @@ const newAccount = reactive({
}); });
const form = reactive({ const form = reactive({
name: '梅派传人小林', name: '',
bio: '专注京剧程派艺术传承与推广', bio: '',
description: '', description: '',
avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Master1', avatar: '',
cover: 'https://images.unsplash.com/photo-1514306191717-452ec28c7f31?ixlib=rb-1.2.1&auto=format&fit=crop&w=1200&q=80' cover: ''
}); });
const fetchData = async () => {
try {
const [settings, accounts] = await Promise.all([
creatorApi.getSettings(),
creatorApi.listPayoutAccounts()
]);
if (settings) {
Object.assign(form, settings);
// Set defaults if empty
if (!form.avatar) form.avatar = 'https://api.dicebear.com/7.x/avataaars/svg?seed=Master1';
}
if (accounts) payoutAccounts.value = accounts;
} catch (e) {
console.error(e);
}
};
onMounted(fetchData);
const triggerUpload = (type) => { const triggerUpload = (type) => {
currentUploadType.value = type; currentUploadType.value = type;
fileInput.value.click(); if (fileInput.value) {
}; fileInput.value.accept = 'image/*';
fileInput.value.click();
const handleFileChange = (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
if (currentUploadType.value === 'avatar') {
form.avatar = e.target.result;
} else {
form.cover = e.target.result;
}
};
reader.readAsDataURL(file);
} }
}; };
const handleAddAccount = () => { const handleFileChange = async (event) => {
showAddAccount.value = false; const file = event.target.files[0];
toast.add({ severity: 'success', summary: '添加成功', detail: '收款账户已添加', life: 3000 }); if (!file) return;
try {
toast.add({ severity: 'info', summary: '正在上传...', life: 2000 });
const task = commonApi.uploadWithProgress(file, 'image');
const res = await task.promise;
console.log('Upload response:', res);
if (res && res.url) {
if (currentUploadType.value === 'avatar') {
form.avatar = res.url;
} else {
form.cover = res.url;
}
toast.add({ severity: 'success', summary: '上传成功', life: 2000 });
} else {
console.error('Invalid upload response:', res);
toast.add({ severity: 'error', summary: '上传失败', detail: '服务器返回无效数据', life: 3000 });
}
} catch (e) {
console.error(e);
toast.add({ severity: 'error', summary: '上传失败', detail: e.message, life: 3000 });
}
event.target.value = '';
}; };
const saveSettings = () => { const handleAddAccount = async () => {
if (!newAccount.name || !newAccount.account || !newAccount.realname) {
toast.add({ severity: 'warn', summary: '提示', detail: '请填写完整信息', life: 3000 });
return;
}
try {
await creatorApi.addPayoutAccount(newAccount);
showAddAccount.value = false;
toast.add({ severity: 'success', summary: '添加成功', detail: '收款账户已添加', life: 3000 });
fetchData();
// Reset
newAccount.name = ''; newAccount.account = ''; newAccount.realname = '';
} catch (e) {
toast.add({ severity: 'error', summary: '添加失败', detail: e.message, life: 3000 });
}
};
const removeAccount = async (id) => {
if (!confirm('确定要删除此账户吗?')) return;
try {
await creatorApi.removePayoutAccount(id);
toast.add({ severity: 'success', summary: '删除成功', life: 3000 });
fetchData();
} catch (e) {
toast.add({ severity: 'error', summary: '删除失败', detail: e.message, life: 3000 });
}
};
const saveSettings = async () => {
if (!form.name) { if (!form.name) {
toast.add({ severity: 'error', summary: '错误', detail: '频道名称不能为空', life: 3000 }); toast.add({ severity: 'error', summary: '错误', detail: '频道名称不能为空', life: 3000 });
return; return;
} }
toast.add({ severity: 'success', summary: '保存成功', detail: '部分信息正在审核中', life: 3000 }); try {
await creatorApi.updateSettings(form);
toast.add({ severity: 'success', summary: '保存成功', detail: '设置已更新', life: 3000 });
} catch (e) {
toast.add({ severity: 'error', summary: '保存失败', detail: e.message, life: 3000 });
}
}; };
</script> </script>