feat: 内容稿件列表、上传内容、通用Textovertips组件封装
This commit is contained in:
BIN
src/assets/img/creative-generation-workshop/icon-photo.png
Normal file
BIN
src/assets/img/creative-generation-workshop/icon-photo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 543 B |
BIN
src/assets/img/creative-generation-workshop/icon-upload-fail.png
Normal file
BIN
src/assets/img/creative-generation-workshop/icon-upload-fail.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
BIN
src/assets/img/creative-generation-workshop/icon-video.png
Normal file
BIN
src/assets/img/creative-generation-workshop/icon-video.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 513 B |
@ -27,9 +27,11 @@
|
||||
|
||||
<script setup>
|
||||
import { useSidebarStore } from '@/stores/modules/side-bar';
|
||||
import router from '@/router';
|
||||
// import router from '@/router';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const sidebarStore = useSidebarStore();
|
||||
const router = useRouter();
|
||||
|
||||
const selectedKey = computed(() => {
|
||||
return [String(sidebarStore.activeMenuId)];
|
||||
|
||||
143
src/components/text-over-tips/index.vue
Normal file
143
src/components/text-over-tips/index.vue
Normal file
@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<a-tooltip :disabled="isShowBtn || (!isShowBtn && disabled)" :placement="props.placement">
|
||||
<template #content>
|
||||
<div :style="contentStyle" class="tip-content">{{ props.context }}</div>
|
||||
</template>
|
||||
<div v-bind="$attrs" class="overflow-hidden">
|
||||
<div ref="Text" :class="`${isShow ? '' : `line-${props.line}`} `" class="overflow-text">
|
||||
{{ props.context }}
|
||||
</div>
|
||||
<div
|
||||
v-if="isShowBtn && !disabled"
|
||||
class="color-#8C8C8C flex items-center cursor-pointer mt-2px"
|
||||
@click="
|
||||
() => {
|
||||
isShow = !isShow;
|
||||
}
|
||||
"
|
||||
>
|
||||
{{ isShow ? '收起' : '展开' }}
|
||||
<icon-up size="16" :class="{ active: isShow }" class="ml-2px color-#8C8C8C" />
|
||||
</div>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, toRefs, onBeforeMount, onMounted, watchEffect, computed, watch, nextTick } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import elementResizeDetectorMaker from 'element-resize-detector';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const props = defineProps({
|
||||
context: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placement: {
|
||||
type: String,
|
||||
default: 'bottom',
|
||||
},
|
||||
line: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
maxHeight: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
maxWidth: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
isShowBtn: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const data = reactive({});
|
||||
const isShow = ref(false);
|
||||
const contentStyle = computed(() => {
|
||||
let style = {
|
||||
'max-height': props.maxHeight + 'px',
|
||||
'max-width': props.maxWidth + 'px',
|
||||
overflow: 'auto',
|
||||
padding: '1px 0',
|
||||
};
|
||||
return props.maxHeight || props.maxWidth ? style : {};
|
||||
});
|
||||
const disabled = ref(true);
|
||||
const Text = ref(null);
|
||||
const textWidth = ref();
|
||||
watch(
|
||||
[() => props.context, textWidth],
|
||||
async () => {
|
||||
if (props.context) {
|
||||
await nextTick();
|
||||
nextTick(() => {
|
||||
if (props.line < 2) {
|
||||
if (Text.value?.clientWidth < Text.value?.scrollWidth) {
|
||||
disabled.value = false;
|
||||
} else {
|
||||
disabled.value = true;
|
||||
}
|
||||
} else {
|
||||
if (Text.value?.clientHeight < Text.value?.scrollHeight) {
|
||||
disabled.value = false;
|
||||
} else {
|
||||
disabled.value = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
onBeforeMount(() => {});
|
||||
onMounted(async () => {
|
||||
await nextTick();
|
||||
const erd = elementResizeDetectorMaker();
|
||||
if (Text.value) {
|
||||
erd.listenTo(Text.value, () => {
|
||||
const _width = Text.value.getBoundingClientRect().width;
|
||||
textWidth.value = _width;
|
||||
});
|
||||
}
|
||||
});
|
||||
watchEffect(() => {});
|
||||
defineExpose({
|
||||
...toRefs(data),
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.overflow-text {
|
||||
display: inline-block;
|
||||
font-family: $font-family-regular;
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
font-style: normal;
|
||||
box-sizing: border-box;
|
||||
white-space: pre-line;
|
||||
&.line-1 {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
&.line-2 {
|
||||
@include multi-ellipsis(2);
|
||||
}
|
||||
&.line-3 {
|
||||
@include multi-ellipsis(3);
|
||||
}
|
||||
}
|
||||
.tip-content {
|
||||
white-space: pre-line;
|
||||
}
|
||||
.active {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
</style>
|
||||
10
src/main.ts
10
src/main.ts
@ -6,8 +6,8 @@ import App from './App.vue';
|
||||
import router from './router';
|
||||
import store from './stores';
|
||||
|
||||
import NoData from '@/components/no-data';
|
||||
import SvgIcon from "@/components/svg-icon";
|
||||
import NoData from '@/components/no-data/index.vue';
|
||||
import SvgIcon from '@/components/svg-icon/index.vue';
|
||||
|
||||
import '@/api/index';
|
||||
import '@arco-design/web-vue/dist/arco.css'; // Arco 默认样式
|
||||
@ -15,7 +15,7 @@ import './core';
|
||||
|
||||
import 'normalize.css';
|
||||
import 'uno.css';
|
||||
import 'virtual:svg-icons-register'
|
||||
import 'virtual:svg-icons-register';
|
||||
|
||||
// import '@/styles/vars.css'; // 优先加载
|
||||
|
||||
@ -24,6 +24,4 @@ const app = createApp(App);
|
||||
app.component('NoData', NoData);
|
||||
app.component('SvgIcon', SvgIcon);
|
||||
|
||||
app.use(store);
|
||||
app.use(router);
|
||||
app.mount('#app');
|
||||
app.use(store).use(router).mount('#app');
|
||||
|
||||
@ -79,7 +79,7 @@ export const useUserStore = defineStore('user', {
|
||||
getUserAllowAccessRoutes() {
|
||||
const sidebarStore = useSidebarStore();
|
||||
const menuList = sidebarStore.menuList;
|
||||
const appRoutes = router.options?.routes ?? [];
|
||||
const appRoutes = router.getRoutes();
|
||||
|
||||
appRoutes.forEach((route: any) => {
|
||||
if (!route.meta?.requiresAuth) {
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
.arco-form {
|
||||
.arco-form-item {
|
||||
margin-bottom: 16px !important;
|
||||
margin-bottom: 0;
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 16px !important;
|
||||
}
|
||||
.arco-form-item-label-col {
|
||||
padding-right: 12px !important;
|
||||
.arco-form-item-label {
|
||||
|
||||
@ -23,3 +23,23 @@ p {
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track-piece {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
height: 10px;
|
||||
background-color: #C9CDD4;
|
||||
border-radius: 99px;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -74,3 +74,27 @@
|
||||
.arco-link {
|
||||
--color-primary-6: var(--arco-primary-6) !important;
|
||||
}
|
||||
|
||||
.common-filter-wrap {
|
||||
padding: 24px 24px 8px;
|
||||
.filter-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
.filter-row-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 24px 16px 0;
|
||||
.label {
|
||||
margin-right: 8px;
|
||||
color: #211f24;
|
||||
font-family: $font-family-regular;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
flex-shrink: 0;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
* @Date: 2025-06-25 14:02:40
|
||||
-->
|
||||
<template>
|
||||
<div class="filter-wrap px-24px pt-12px pb-24px">
|
||||
<div class="filter-row flex">
|
||||
<div class="filter-row-item flex items-center">
|
||||
<div class="common-filter-wrap">
|
||||
<div class="filter-row">
|
||||
<div class="filter-row-item">
|
||||
<span class="label">内容稿件标题</span>
|
||||
<a-space size="medium">
|
||||
<a-input
|
||||
@ -23,7 +23,7 @@
|
||||
</a-input>
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<div class="filter-row-item">
|
||||
<a-button type="outline" class="mr-12px" size="medium" @click="handleSearch">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
@ -64,7 +64,3 @@ const handleReset = () => {
|
||||
emits('reset');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './style.scss';
|
||||
</style>
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
.filter-wrap {
|
||||
.filter-row {
|
||||
.filter-row-item {
|
||||
&:not(:last-child) {
|
||||
margin-right: 24px;
|
||||
}
|
||||
.label {
|
||||
margin-right: 8px;
|
||||
color: #211f24;
|
||||
font-family: $font-family-regular;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
flex-shrink: 0;
|
||||
line-height: 22px; /* 157.143% */
|
||||
}
|
||||
:deep(.arco-space-item) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="filter-wrap px-24px py-24px">
|
||||
<div class="filter-row flex mb-16px">
|
||||
<div class="filter-row-item flex items-center">
|
||||
<div class="common-filter-wrap">
|
||||
<div class="filter-row">
|
||||
<div class="filter-row-item">
|
||||
<span class="label">内容稿件标题</span>
|
||||
<a-input
|
||||
v-model="query.name"
|
||||
v-model="query.title"
|
||||
class="!w-240px"
|
||||
placeholder="请输入内容稿件标题"
|
||||
size="medium"
|
||||
@ -16,15 +16,15 @@
|
||||
</template>
|
||||
</a-input>
|
||||
</div>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<div class="filter-row-item">
|
||||
<span class="label">所属项目</span>
|
||||
<CommonSelect placeholder="请选择所属项目" v-model="query.project_id" class="!w-166px" />
|
||||
</div>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<div class="filter-row-item">
|
||||
<span class="label">序号</span>
|
||||
<a-space size="medium">
|
||||
<a-input
|
||||
v-model="query.number"
|
||||
v-model="query.uid"
|
||||
class="w-160px"
|
||||
placeholder="请输入序号"
|
||||
size="medium"
|
||||
@ -37,14 +37,12 @@
|
||||
</a-input>
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<div class="filter-row-item">
|
||||
<span class="label">审核状态</span>
|
||||
<CommonSelect placeholder="请选择审核状态" v-model="query.status" class="!w-166px" />
|
||||
<CommonSelect placeholder="请选择审核状态" v-model="query.audit_status" class="!w-166px" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-row flex">
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">发布日期</span>
|
||||
<div class="filter-row-item">
|
||||
<span class="label">上传时间</span>
|
||||
<a-range-picker
|
||||
v-model="published_at"
|
||||
size="medium"
|
||||
@ -54,7 +52,7 @@
|
||||
@change="onDateChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<div class="filter-row-item">
|
||||
<a-button type="outline" class="mr-12px" size="medium" @click="handleSearch">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
@ -84,7 +82,7 @@ const props = defineProps({
|
||||
|
||||
const emits = defineEmits('search', 'reset', 'update:query');
|
||||
|
||||
const published_at = ref([]);
|
||||
const created_at = ref([]);
|
||||
|
||||
const handleSearch = () => {
|
||||
emits('update:query', props.query);
|
||||
@ -96,7 +94,7 @@ const handleSearch = () => {
|
||||
const onDateChange = (value) => {
|
||||
const [start, end] = value;
|
||||
const FORMAT_DATE = 'YYYY-MM-DD HH:mm:ss';
|
||||
props.query.published_at = [
|
||||
props.query.created_at = [
|
||||
dayjs(start).startOf('day').format(FORMAT_DATE),
|
||||
dayjs(end).endOf('day').format(FORMAT_DATE),
|
||||
];
|
||||
@ -105,11 +103,7 @@ const onDateChange = (value) => {
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
published_at.value = [];
|
||||
created_at.value = [];
|
||||
emits('reset');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './style.scss';
|
||||
</style>
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
.filter-wrap {
|
||||
.filter-row {
|
||||
.filter-row-item {
|
||||
&:not(:last-child) {
|
||||
margin-right: 24px;
|
||||
}
|
||||
.label {
|
||||
margin-right: 8px;
|
||||
color: #211f24;
|
||||
font-family: $font-family-regular;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
flex-shrink: 0;
|
||||
line-height: 22px; /* 157.143% */
|
||||
}
|
||||
:deep(.arco-space-item) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -4,6 +4,9 @@ export const TABLE_COLUMNS = [
|
||||
dataIndex: 'index',
|
||||
width: 120,
|
||||
fixed: 'left',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '图片/视频',
|
||||
@ -12,12 +15,12 @@ export const TABLE_COLUMNS = [
|
||||
},
|
||||
{
|
||||
title: '内容稿件标题',
|
||||
dataIndex: 'name',
|
||||
dataIndex: 'manuscript_name',
|
||||
width: 240,
|
||||
},
|
||||
{
|
||||
title: '所属项目',
|
||||
dataIndex: 'name',
|
||||
dataIndex: 'project_name',
|
||||
width: 240,
|
||||
},
|
||||
{
|
||||
@ -27,7 +30,7 @@ export const TABLE_COLUMNS = [
|
||||
},
|
||||
{
|
||||
title: '审核状态',
|
||||
dataIndex: 'media_account_count',
|
||||
dataIndex: 'status',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
@sorter-change="handleSorterChange"
|
||||
>
|
||||
<template #empty>
|
||||
<NoData text="暂无稿件"/>
|
||||
<NoData text="暂无稿件" />
|
||||
</template>
|
||||
<template #columns>
|
||||
<a-table-column
|
||||
@ -38,14 +38,43 @@
|
||||
<template v-if="column.dataIndex === 'create_at'" #cell="{ record }">
|
||||
{{ exactFormatTime(record.create_at) }}
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'manuscript_name'" #cell="{ record }">
|
||||
<TextOverTips :context="record.name" :line="3" class="manuscript_name" />
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'project_name'" #cell="{ record }">
|
||||
<TextOverTips :context="record.project_name" :line="3" />
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'status'" #cell="{ record }">
|
||||
<div
|
||||
class="flex items-center w-fit h-28px px-8px rounded-2px"
|
||||
:style="{ backgroundColor: getStatusInfo(record.status).backgroundColor }"
|
||||
>
|
||||
<span class="cts s1" :style="{ color: getStatusInfo(record.status).color }">{{
|
||||
getStatusInfo(record.status).label
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'budget'" #cell="{ record }">
|
||||
<div class="flex items-center">
|
||||
<img :src="record.budget === 1 ? icon2 : icon3" width="16" height="16" class="mr-4px" />
|
||||
<span class="cts" :class="record.budget === 1 ? '!color-#25C883' : '!color-#6D4CFE'">{{
|
||||
record.budget === 1 ? '图文' : '视频'
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="['create_at', 'last_create_at'].includes(column.dataIndex)" #cell="{ record }">
|
||||
{{ exactFormatTime(record[column.dataIndex]) }}
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'picker'" #cell="{ record }">
|
||||
<a-image :width="64" :height="64" :src="record.picker" class="rounded-6px" />
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'" #cell="{ record }">
|
||||
<div class="flex items-center">
|
||||
<img class="mr-8px cursor-pointer" :src="icon1" width="14" height="14" @click="onDelete(record)" />
|
||||
<a-button type="outline" size="mini" @click="onEdit(record)">编辑</a-button>
|
||||
<a-button type="outline" size="mini" class="mr-8px" @click="onEdit(record)">编辑</a-button>
|
||||
<a-button type="outline" size="mini" @click="onDetail(record)">详情</a-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else #cell="{ record }">
|
||||
{{ formatTableField(column, record, true) }}
|
||||
</template>
|
||||
@ -58,8 +87,13 @@
|
||||
import { ref } from 'vue';
|
||||
import { formatTableField, exactFormatTime } from '@/utils/tools';
|
||||
import { TABLE_COLUMNS } from './constants';
|
||||
import { CHECK_STATUS } from '@/views/creative-generation-workshop/manuscript/manuscript-list/constants';
|
||||
|
||||
import TextOverTips from '@/components/text-over-tips';
|
||||
|
||||
import icon1 from '@/assets/img/media-account/icon-delete.png';
|
||||
import icon2 from '@/assets/img/creative-generation-workshop/icon-photo.png';
|
||||
import icon3 from '@/assets/img/creative-generation-workshop/icon-video.png';
|
||||
|
||||
const emits = defineEmits(['edit', 'sorterChange', 'delete']);
|
||||
|
||||
@ -82,7 +116,11 @@ const onEdit = (item) => {
|
||||
emits('edit', item);
|
||||
};
|
||||
const onDetail = (item) => {
|
||||
console.log('onDetail')
|
||||
console.log('onDetail');
|
||||
};
|
||||
|
||||
const getStatusInfo = (status) => {
|
||||
return CHECK_STATUS.find((v) => v.value === status) ?? {};
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@ -7,4 +7,10 @@
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
:deep(.manuscript_name) {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #6d4cfe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,320 @@
|
||||
<script lang="jsx">
|
||||
import {
|
||||
Modal,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
RadioGroup,
|
||||
Radio,
|
||||
Upload,
|
||||
Button,
|
||||
Tooltip,
|
||||
Message as AMessage,
|
||||
Textarea,
|
||||
} from '@arco-design/web-vue';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
|
||||
import TextOverTips from '@/components/text-over-tips';
|
||||
|
||||
import icon1 from '@/assets/img/media-account/icon-feedback-fail.png';
|
||||
|
||||
const INITIAL_FORM = {
|
||||
linkUrl: '',
|
||||
link: '',
|
||||
};
|
||||
const FORM_TASK_STATUS = {
|
||||
default: 1, // 默认状态,
|
||||
importLoading: 2, // 上传中
|
||||
importFailed: 3, // 上传失败
|
||||
importSuccess: 4, // 上传成功
|
||||
};
|
||||
|
||||
export default {
|
||||
setup(props, { emit, expose }) {
|
||||
const update = inject('update');
|
||||
const visible = ref(false);
|
||||
const formRef = ref(null);
|
||||
const uploadRef = ref(null);
|
||||
const uploadType = ref('link');
|
||||
const taskStatus = ref(FORM_TASK_STATUS.default);
|
||||
const form = ref(cloneDeep(INITIAL_FORM));
|
||||
|
||||
const { copy, copied, isSupported } = useClipboard({ source: form.value.link });
|
||||
|
||||
const isLink = computed(() => uploadType.value === 'link'); // 链接上传
|
||||
const isLocal = computed(() => uploadType.value === 'local'); // 本地上传
|
||||
const isHandwrite = computed(() => uploadType.value === 'handwrite'); // 手写上传
|
||||
|
||||
const isDefaultTask = computed(() => taskStatus.value === FORM_TASK_STATUS.default);
|
||||
const isImportLoadingTask = computed(() => taskStatus.value === FORM_TASK_STATUS.importLoading);
|
||||
const isImportFailedTask = computed(() => taskStatus.value === FORM_TASK_STATUS.importFailed);
|
||||
const isImportSuccessTask = computed(() => taskStatus.value === FORM_TASK_STATUS.importSuccess);
|
||||
|
||||
const open = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
const reset = () => {
|
||||
formRef.value?.resetFields?.();
|
||||
uploadType.value = 'link';
|
||||
taskStatus.value = FORM_TASK_STATUS.default;
|
||||
form.value = cloneDeep(INITIAL_FORM);
|
||||
};
|
||||
const onClose = () => {
|
||||
reset();
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
if (isHandwrite.value) {
|
||||
if (!isSupported) {
|
||||
AMessage.error('您的浏览器不支持复制,请手动复制!');
|
||||
}
|
||||
copy(form.value.link);
|
||||
if (!copied) {
|
||||
AMessage.error('复制失败,请手动复制!');
|
||||
}
|
||||
AMessage.success('复制成功!');
|
||||
onClose();
|
||||
return;
|
||||
}
|
||||
|
||||
taskStatus.value = FORM_TASK_STATUS.importLoading;
|
||||
setTimeout(() => {
|
||||
taskStatus.value = FORM_TASK_STATUS.importSuccess;
|
||||
}, 2000);
|
||||
};
|
||||
const onCancelUpload = () => {
|
||||
console.log('onCancelUpload');
|
||||
taskStatus.value = FORM_TASK_STATUS.default;
|
||||
// onClose()
|
||||
};
|
||||
|
||||
const handleUpload = async (file) => {
|
||||
console.log('handleUpload11111', file);
|
||||
taskStatus.value = FORM_TASK_STATUS.importLoading;
|
||||
setTimeout(() => {
|
||||
taskStatus.value = FORM_TASK_STATUS.importSuccess;
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const goEdit = () => {
|
||||
console.log('goEdit 去稿件编辑页面');
|
||||
};
|
||||
|
||||
const onDelete = (item) => {
|
||||
console.log('onDelete', item);
|
||||
};
|
||||
|
||||
const handleDownloadTemplate = async () => {
|
||||
// const { code, data } = await getPlacementAccountsTemplateUrl();
|
||||
// if (code === 200) {
|
||||
// window.open(data.download_url, '_blank');
|
||||
// }
|
||||
};
|
||||
const getLink = () => {
|
||||
return (
|
||||
<FormItem label="链接地址">
|
||||
<Textarea
|
||||
v-model={form.value.linkUrl}
|
||||
size="large"
|
||||
placeholder="请输入..."
|
||||
autoSize={{ minRows: 5, maxRows: 8 }}
|
||||
/>
|
||||
</FormItem>
|
||||
);
|
||||
};
|
||||
|
||||
const getHandwrite = () => {
|
||||
return (
|
||||
<FormItem label="上传链接">
|
||||
<Input v-model={form.value.link} placeholder="请输入..." size="large" />
|
||||
</FormItem>
|
||||
);
|
||||
};
|
||||
|
||||
const getLocal = () => {
|
||||
return (
|
||||
<FormItem label="内容稿件">
|
||||
<div class="flex flex-col w-full">
|
||||
<Upload
|
||||
ref="uploadRef"
|
||||
action="/"
|
||||
draggable
|
||||
customRequest={handleUpload}
|
||||
accept=".xlsx,.xls,.docx,.doc"
|
||||
show-file-list={false}
|
||||
>
|
||||
{{
|
||||
'upload-button': () => (
|
||||
<div class="upload-box">
|
||||
<icon-plus size="14" class="mb-16px" />
|
||||
<span class="text mb-4px">点击或拖拽文件到此处上传</span>
|
||||
<span class="tip">支持文档(文本+图), 视频批量上传</span>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
</Upload>
|
||||
<div class="flex items-center cursor-pointer mt-8px" onClick={handleDownloadTemplate}>
|
||||
<icon-download size="14" class="mr-4px !color-#6D4CFE" />
|
||||
<span class="cts color-#6D4CFE">下载示例文档</span>
|
||||
</div>
|
||||
</div>
|
||||
</FormItem>
|
||||
);
|
||||
};
|
||||
|
||||
const renderFormContent = () => {
|
||||
if (isDefaultTask.value) {
|
||||
if (isLink.value) {
|
||||
return getLink();
|
||||
}
|
||||
if (isHandwrite.value) {
|
||||
return getHandwrite();
|
||||
}
|
||||
if (isLocal.value) {
|
||||
return getLocal();
|
||||
}
|
||||
}
|
||||
|
||||
if (isImportLoadingTask.value) {
|
||||
return (
|
||||
<div class="flex flex-col items-center justify-center rounded-8px bg-#F7F8FA h-208px">
|
||||
<icon-loading size="48" class="!color-#6D4CFE mb-16px" />
|
||||
<p class="tip !text-#768893">上传过程耗时可能较长,请耐心等待;</p>
|
||||
<p class="tip !text-#768893">刷新页面将会终止本次数据的上传,请谨慎操作</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isImportFailedTask.value) {
|
||||
return (
|
||||
<div class="flex flex-col items-center justify-center rounded-8px bg-#F7F8FA h-208px">
|
||||
<img src={icon1} class="w-80px h-80px mb-16px" />
|
||||
<p class="text mb-4px">上传失败</p>
|
||||
<p class="tip !text-#768893">可能是网络不稳定导致,请检查网络后重试~</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isImportSuccessTask.value) {
|
||||
return (
|
||||
<div class="flex flex-col py-12px max-h-540px rounded-8px bg-#F7F8FA">
|
||||
<span class="tip mb-8px px-12px fs-14px !text-left">共 30 个内容稿件</span>
|
||||
<div class="flex-1 overflow-y-auto overflow-x-hidden px-12px">
|
||||
{new Array(20).fill(0).map((v) => (
|
||||
<div class="rounded-8px bg-#fff px-8px py-8px flex justify-between items-center mt-8px">
|
||||
<div class="flex-1 overflow-hidden flex items-center mr-12px">
|
||||
<img width="32" height="32" class="rounded-3px mr-8px" />
|
||||
<TextOverTips
|
||||
class="text"
|
||||
context="挖到宝了!这个平价好物让我素颜出门都自信✨挖到宝了!这个平价好物让我素颜出门都自信✨"
|
||||
/>
|
||||
</div>
|
||||
<icon-delete
|
||||
size="16px"
|
||||
class="color-#737478 cursor-pointer hover:!color-#211F24"
|
||||
onClick={onDelete}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const renderFooterBtn = () => {
|
||||
if (isImportLoadingTask.value) {
|
||||
return (
|
||||
<Button type="primary" size="medium" onClick={onCancelUpload}>
|
||||
取消上传
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
const getConfirmBtn = () => {
|
||||
if (isDefaultTask.value) {
|
||||
return (
|
||||
<Button type="primary" size="medium" onClick={onSubmit}>
|
||||
{isHandwrite.value ? '复制链接' : '确认'}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
if (isImportFailedTask.value) {
|
||||
return (
|
||||
<Button type="primary" size="medium" onClick={onClose}>
|
||||
重新上传
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
if (isImportSuccessTask.value) {
|
||||
return (
|
||||
<Button type="primary" size="medium" onClick={goEdit}>
|
||||
确认
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button size="medium" onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
{getConfirmBtn()}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getTitle = () => {
|
||||
if (isDefaultTask.value) {
|
||||
return '上传内容稿件';
|
||||
} else if (isImportSuccessTask.value) {
|
||||
return '上传内容稿件列表';
|
||||
} else {
|
||||
if (isLink.value) {
|
||||
return '链接上传';
|
||||
}
|
||||
if (isLocal.value) {
|
||||
return '本地批量上传';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
expose({ open });
|
||||
|
||||
return () => (
|
||||
<Modal
|
||||
v-model:visible={visible.value}
|
||||
title={getTitle()}
|
||||
modal-class="upload-manuscript-modal"
|
||||
width="500px"
|
||||
mask-closable={false}
|
||||
unmount-on-close
|
||||
onClose={onClose}
|
||||
footer={!(isDefaultTask.value && isLocal.value)}
|
||||
v-slots={{
|
||||
footer: () => renderFooterBtn(),
|
||||
}}
|
||||
>
|
||||
<Form ref="formRef" model={form.value} layout="horizontal" auto-label-width>
|
||||
{isDefaultTask.value && (
|
||||
<FormItem label="上传方式">
|
||||
<RadioGroup v-model={uploadType.value}>
|
||||
<Radio value="link">链接上传</Radio>
|
||||
<Radio value="local">本地上传</Radio>
|
||||
<Radio value="handwrite">手写上传</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
)}
|
||||
|
||||
{renderFormContent()}
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@import './style.scss';
|
||||
</style>
|
||||
@ -0,0 +1,31 @@
|
||||
.upload-manuscript-modal {
|
||||
.text {
|
||||
color: var(--Text-1, #211f24);
|
||||
text-align: center;
|
||||
font-family: $font-family-regular;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
.tip {
|
||||
color: var(--Text-3, #737478);
|
||||
text-align: center;
|
||||
font-family: $font-family-regular;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
.upload-box {
|
||||
display: flex;
|
||||
height: 120px;
|
||||
padding: 0 16px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 2px;
|
||||
border: 1px dashed var(--Border-1, #d7d7d9);
|
||||
background: var(--BG-200, #f2f3f5);
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,60 @@
|
||||
export const INITIAL_QUERY = {
|
||||
name: '',
|
||||
project_id: '',
|
||||
number: '',
|
||||
status: '',
|
||||
published_at: [],
|
||||
title: '',
|
||||
project_ids: [],
|
||||
uid: '',
|
||||
audit_status: '',
|
||||
created_at: [],
|
||||
sort_column: undefined,
|
||||
sort_order: undefined,
|
||||
};
|
||||
|
||||
export enum EnumCheckStatus {
|
||||
All = '',
|
||||
Wait = 0,
|
||||
Checking = 1,
|
||||
Passed = 2,
|
||||
}
|
||||
export enum EnumManuscriptType {
|
||||
All = '',
|
||||
Image = 1,
|
||||
Video = 2,
|
||||
}
|
||||
|
||||
export const CHECK_STATUS = [
|
||||
{
|
||||
label: '全部',
|
||||
value: EnumCheckStatus.All,
|
||||
},
|
||||
{
|
||||
label: '待审核',
|
||||
value: EnumCheckStatus.Wait,
|
||||
backgroundColor: '#F2F3F5',
|
||||
color: '#3C4043'
|
||||
},
|
||||
{
|
||||
label: '审核中',
|
||||
value: EnumCheckStatus.Checking,
|
||||
backgroundColor: '#FFF7E5',
|
||||
color: '#FFAE00'
|
||||
},
|
||||
{
|
||||
label: '已通过',
|
||||
value: EnumCheckStatus.Passed,
|
||||
backgroundColor: '#EBF7F2',
|
||||
color: '#25C883'
|
||||
},
|
||||
];
|
||||
export const MANUSCRIPT_TYPE = [
|
||||
{
|
||||
label: '全部',
|
||||
value: EnumManuscriptType.All,
|
||||
},
|
||||
{
|
||||
label: '图片',
|
||||
value: EnumManuscriptType.Image,
|
||||
},
|
||||
{
|
||||
label: '视频',
|
||||
value: EnumManuscriptType.Video,
|
||||
},
|
||||
]
|
||||
|
||||
@ -4,8 +4,10 @@
|
||||
<div class="top flex h-64px px-24px py-10px justify-between items-center">
|
||||
<p class="text-18px font-400 lh-26px color-#211F24 title">内容稿件列表</p>
|
||||
<div class="flex items-center">
|
||||
<a-button type="outline" size="medium" class="mr-12px" @click="handleOpenAddProjectModal"> 分享内容稿件 </a-button>
|
||||
<a-button type="primary" size="medium" @click="handleOpenAddProjectModal">
|
||||
<a-button type="outline" size="medium" class="mr-12px" @click="handleOpenAddProjectModal">
|
||||
分享内容稿件
|
||||
</a-button>
|
||||
<a-button type="primary" size="medium" @click="openUploadModal">
|
||||
<template #icon>
|
||||
<icon-plus size="16" />
|
||||
</template>
|
||||
@ -40,6 +42,7 @@
|
||||
</div>
|
||||
|
||||
<DeleteManuscriptModal ref="deleteManuscriptModalRef" />
|
||||
<UploadManuscriptModal ref="uploadManuscriptModalRef" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="jsx" setup>
|
||||
@ -48,10 +51,14 @@ import { Button } from '@arco-design/web-vue';
|
||||
import FilterBlock from './components/filter-block';
|
||||
import ManuscriptTable from './components/manuscript-table';
|
||||
import DeleteManuscriptModal from './components/manuscript-table/delete-manuscript-modal.vue';
|
||||
import UploadManuscriptModal from './components/upload-manascript-modal';
|
||||
|
||||
import { useTableSelectionWithPagination } from '@/hooks/useTableSelectionWithPagination';
|
||||
import { getProjects } from '@/api/all/propertyMarketing';
|
||||
import { INITIAL_QUERY } from './constants';
|
||||
import {
|
||||
INITIAL_QUERY,
|
||||
EnumCheckStatus,
|
||||
} from '@/views/creative-generation-workshop/manuscript/manuscript-list/constants.ts';
|
||||
|
||||
const { dataSource, pageInfo, onPageChange, onPageSizeChange, resetPageInfo } = useTableSelectionWithPagination({
|
||||
onPageChange: () => {
|
||||
@ -64,18 +71,46 @@ const { dataSource, pageInfo, onPageChange, onPageSizeChange, resetPageInfo } =
|
||||
const query = ref(cloneDeep(INITIAL_QUERY));
|
||||
const addManuscriptModalRef = ref(null);
|
||||
const deleteManuscriptModalRef = ref(null);
|
||||
const uploadManuscriptModalRef = ref(null);
|
||||
|
||||
const getData = async () => {
|
||||
const { page, page_size } = pageInfo.value;
|
||||
const { code, data } = await getProjects({
|
||||
...query.value,
|
||||
page,
|
||||
page_size,
|
||||
});
|
||||
if (code === 200) {
|
||||
dataSource.value = data?.data ?? [];
|
||||
pageInfo.value.total = data.total;
|
||||
}
|
||||
dataSource.value = [
|
||||
{
|
||||
index: 1,
|
||||
name: '内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1',
|
||||
project_name: '内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1',
|
||||
status: EnumCheckStatus.Wait,
|
||||
budget: 1,
|
||||
create_at: 1753682671,
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
name: '内容稿件2',
|
||||
project_name: '内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1',
|
||||
status: EnumCheckStatus.Checking,
|
||||
budget: 2,
|
||||
create_at: 1753682672,
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
name: '内容稿件3',
|
||||
project_name: '内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1内容稿件1',
|
||||
status: EnumCheckStatus.Passed,
|
||||
budget: 1,
|
||||
create_at: 1753682673,
|
||||
},
|
||||
];
|
||||
pageInfo.value.total = 3;
|
||||
// const { page, page_size } = pageInfo.value;
|
||||
// const { code, data } = await getProjects({
|
||||
// ...query.value,
|
||||
// page,
|
||||
// page_size,
|
||||
// });
|
||||
// if (code === 200) {
|
||||
// dataSource.value = data?.data ?? [];
|
||||
// pageInfo.value.total = data.total;
|
||||
// }
|
||||
};
|
||||
const handleSearch = () => {
|
||||
reload();
|
||||
@ -98,6 +133,9 @@ const handleSorterChange = (column, order) => {
|
||||
const handleOpenAddProjectModal = () => {
|
||||
console.log('handleOpenAddProjectModal');
|
||||
};
|
||||
const openUploadModal = () => {
|
||||
uploadManuscriptModalRef.value?.open();
|
||||
};
|
||||
|
||||
const handleDelete = (item) => {
|
||||
const { id, name } = item;
|
||||
@ -114,5 +152,5 @@ provide('update', getData);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./style.scss"
|
||||
@import './style.scss';
|
||||
</style>
|
||||
|
||||
@ -308,7 +308,7 @@ const handleBatchImport = async () => {
|
||||
if (code === 200) {
|
||||
const id = genRandomId();
|
||||
showExportNotification(`正在导入“${file.value.name}”,请稍后...`, { id });
|
||||
emit('startQueryTaskStatus', data.id, id);
|
||||
emits('startQueryTaskStatus', data.id, id);
|
||||
onClose();
|
||||
} else {
|
||||
uploadStatus.value = UploadStatus.ERROR;
|
||||
|
||||
Reference in New Issue
Block a user