review:【antd】【mp】图文草稿箱

This commit is contained in:
YunaiV
2025-11-09 17:16:51 +08:00
parent d6cbad72e5
commit 229ead48f1
7 changed files with 52 additions and 49 deletions

View File

@@ -149,6 +149,7 @@ const showCreateButton = computed(() => {
return tableDataLength.value <= 0;
});
// TODO @hw看看能不能参考 tag/index.vue 简化下
/** 页面挂载后,等待表单初始化完成再加载数据 */
onMounted(async () => {
// 等待 WxAccountSelect 组件加载并设置默认值

View File

@@ -13,8 +13,6 @@ import { Button, Image, message, Modal, Upload } from 'ant-design-vue';
import { UploadType, useBeforeUpload } from '#/utils/useUpload';
import { WxMaterialSelect } from '#/views/mp/modules/wx-material-select';
// 设置上传的请求头部
const props = defineProps<{
isFirst: boolean;
modelValue: NewsItem;
@@ -54,10 +52,11 @@ function onMaterialSelected(item: any) {
newsItem.value.thumbMediaId = item.mediaId;
newsItem.value.thumbUrl = item.url;
}
// TODO @hw注释都补充下哈
const onBeforeUpload = (file: UploadFile) =>
useBeforeUpload(UploadType.Image, 2)(file as any);
// TODO @hw注释都补充下哈
function onUploadChange(info: any) {
if (info.file.status === 'done') {
onUploadSuccess(info.file.response || info.file);
@@ -66,6 +65,7 @@ function onUploadChange(info: any) {
}
}
// TODO @hw注释都补充下哈
function onUploadSuccess(res: any) {
if (res.code !== 0) {
message.error(`上传出错:${res.msg}`);
@@ -74,12 +74,12 @@ function onUploadSuccess(res: any) {
// 重置上传文件的表单
fileList.value = [];
// 设置草稿的封面字段
newsItem.value.thumbMediaId = res.data.mediaId;
newsItem.value.thumbUrl = res.data.url;
}
// TODO @hw注释都补充下哈
function onUploadError(err: Error) {
message.error(`上传失败: ${err.message}`);
}
@@ -88,6 +88,7 @@ function onUploadError(err: Error) {
<template>
<div>
<p>封面:</p>
<!-- TODO @hw我貌似上传不成功不确定是不是我这边的问题可以微信沟通下哈 -->
<div class="thumb-div">
<Image
v-if="newsItem.thumbUrl"
@@ -115,6 +116,7 @@ function onUploadError(err: Error) {
<Button size="small" type="primary">本地上传</Button>
</template>
</Upload>
<!-- TODO @hwtindwind -->
<Button
size="small"
type="primary"
@@ -146,6 +148,7 @@ function onUploadError(err: Error) {
</template>
<style lang="scss" scoped>
/** TODO @hw尽量使用 tindwind 替代。ps如果多个组件复用那就不用调整 */
.upload-tip {
margin-top: 5px;
margin-left: 5px;

View File

@@ -20,7 +20,6 @@ const props = defineProps<{
modelValue: NewsItem[] | null;
}>();
// v-model=newsList
const emit = defineEmits<{
(e: 'update:modelValue', v: NewsItem[]): void;
}>();
@@ -45,6 +44,7 @@ const activeNewsItem = computed(() => {
return item;
});
// TODO @hw注释使用 /** */
// 将图文向下移动
function moveDownNews(index: number) {
const current = newsList.value[index];
@@ -69,14 +69,10 @@ function moveUpNews(index: number) {
// 删除指定 index 的图文
async function removeNews(index: number) {
try {
await confirm('确定删除该图文吗?');
newsList.value.splice(index, 1);
if (activeNewsIndex.value === index) {
activeNewsIndex.value = 0;
}
} catch {
// empty
await confirm('确定删除该图文吗?');
newsList.value.splice(index, 1);
if (activeNewsIndex.value === index) {
activeNewsIndex.value = 0;
}
}
@@ -234,6 +230,7 @@ function plusNews() {
</template>
<style lang="scss" scoped>
/** TODO @hw尽量使用 tindwind 替代。ps如果多个组件复用那就不用调整 */
.ope-row {
padding-top: 5px;
margin-top: 5px;

View File

@@ -1,3 +1,4 @@
// TODO @hw要不把 components 里的部分,拿到 modules 里。
interface NewsItem {
title: string;
thumbMediaId: string;

View File

@@ -30,6 +30,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
}
/** 列表的搜索表单 */
// TODO @hw这里的公众号选择要改参考 /Users/yunai/Java/yudao-ui-admin-vben-v5/apps/web-antd/src/views/mp/tag/data.ts相关联的代码还简单点~
export function useGridFormSchema(): VbenFormSchema[] {
return [
{

View File

@@ -6,6 +6,7 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { nextTick, onMounted, provide, ref, watch } from 'vue';
import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { message } from 'ant-design-vue';
@@ -25,6 +26,7 @@ const [FormModal, formModalApi] = useVbenModal({
destroyOnClose: true,
});
// TODO @hw下面的方法放到这个前面和别的保持一致
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
@@ -47,6 +49,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
...formValues,
});
// 处理 API 返回的数据,兼容不同的数据结构
// TODO @wx看 yudao-ui-admin-vue3/src/views/mp/draft/index.vue 项目里,转换没这么复杂。。。是不是这里有办法简化下?
const formattedList: Article[] = drafts.list.map((draft: any) => {
// 如果已经是 content.newsItem 格式,直接使用
if (draft.content?.newsItem) {
@@ -116,9 +119,11 @@ const [Grid, gridApi] = useVbenVxeGrid({
});
// 提供 accountId 给子组件
// TODO @hw参考 tag/index.vue 放到 formValues.accountId;
const accountId = ref<number>(-1);
// 监听表单提交,更新 accountId
// TODO @hw看看这个 watch、provide 能不能简化掉;
watch(
() => gridApi.formApi?.getLatestSubmissionValues?.()?.accountId,
(newAccountId) => {
@@ -138,7 +143,6 @@ async function handleCreate() {
message.warning('请先选择公众号');
return;
}
formModalApi
.setData({
isCreating: true,
@@ -156,7 +160,6 @@ async function handleEdit(row: Article) {
message.warning('请先选择公众号');
return;
}
formModalApi
.setData({
isCreating: false,
@@ -171,30 +174,27 @@ async function handleEdit(row: Article) {
async function handlePublish(row: Article) {
const formValues = await gridApi.formApi.getValues();
const accountId = formValues.accountId;
// TODO @hw看看能不能去掉 -1 的判断哈?
if (!accountId || accountId === -1) {
message.warning('请先选择公众号');
return;
}
const content =
await confirm(
'你正在通过发布的方式发表内容。 发布不占用群发次数,一天可多次发布。' +
'已发布内容不会推送给用户,也不会展示在公众号主页中。 ' +
'发布后,你可以前往发表记录获取链接,也可以将发布内容添加到自定义菜单、自动回复、话题和页面模板中。';
'已发布内容不会推送给用户,也不会展示在公众号主页中。 ' +
'发布后,你可以前往发表记录获取链接,也可以将发布内容添加到自定义菜单、自动回复、话题和页面模板中。',
);
const hideLoading = message.loading({
content: '发布中...',
duration: 0,
});
// TODO @hwMpFreePublishApi 去掉,直接 import参考别的模块哈
try {
await confirm(content);
const hideLoading = message.loading({
content: '发布中...',
duration: 0,
});
try {
await MpFreePublishApi.submitFreePublish(accountId, row.mediaId);
message.success('发布成功');
await gridApi.query();
} finally {
hideLoading();
}
} catch {
//
await MpFreePublishApi.submitFreePublish(accountId, row.mediaId);
message.success('发布成功');
await gridApi.query();
} finally {
hideLoading();
}
}
@@ -206,25 +206,21 @@ async function handleDelete(row: Article) {
message.warning('请先选择公众号');
return;
}
await confirm('此操作将永久删除该草稿, 是否继续?');
const hideLoading = message.loading({
content: '删除中...',
duration: 0,
});
try {
await confirm('此操作将永久删除该草稿, 是否继续?');
const hideLoading = message.loading({
content: '删除中...',
duration: 0,
});
try {
await MpDraftApi.deleteDraft(accountId, row.mediaId);
message.success('删除成功');
await gridApi.query();
} finally {
hideLoading();
}
} catch {
//
await MpDraftApi.deleteDraft(accountId, row.mediaId);
message.success('删除成功');
await gridApi.query();
} finally {
hideLoading();
}
}
// TODO @hw看看能不能参考 tag/index.vue 简化下
// 页面挂载后,等待表单初始化完成再加载数据
onMounted(async () => {
await nextTick();
@@ -243,6 +239,7 @@ onMounted(async () => {
<Page auto-content-height>
<DocAlert title="公众号图文" url="https://doc.iocoder.cn/mp/article/" />
<!-- TODO @hw参考别的模块 @success 调用 refresh 方法 -->
<FormModal
@success="
() => {
@@ -256,7 +253,7 @@ onMounted(async () => {
<TableAction
:actions="[
{
label: '新增',
label: $t('ui.actionTitle.create', ['图文草稿']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['mp:draft:create'],

View File

@@ -15,12 +15,15 @@ const emit = defineEmits(['success']);
const formData = ref<{
accountId: number;
// TODO @hw是不是通过 id 字段判断是否为新增?类似 /Users/yunai/Java/yudao-ui-admin-vben-v5/apps/web-antd/src/views/system/user/modules/form.vue
isCreating: boolean;
mediaId?: string;
newsList?: NewsItem[];
}>();
const newsList = ref<NewsItem[]>([]);
// TODO @hw不需要 isSave通过 modal 去 lock 就好啦。
const isSubmitting = ref(false);
// TODO @hw不需要 isSave通过 modal 去 lock 就好啦。
const isSaved = ref(false);
const getTitle = computed(() => {