Commit b1b30785 by haojie

1

parent c5f8a27f
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import CustomInput from '@/components/custom/input/index.vue'; import CustomInput from '@/components/custom/input/index.vue';
import { ref } from 'vue'; import { ref, watch } from 'vue';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
list: any[]; list: any[];
...@@ -43,8 +43,21 @@ const emit = defineEmits(['update:modelValue']); ...@@ -43,8 +43,21 @@ const emit = defineEmits(['update:modelValue']);
// 当前选择的下标 // 当前选择的下标
const Current_btn = ref(props.modelValue); const Current_btn = ref(props.modelValue);
const onBtnChange = (item: any) => { const onBtnChange = (item: any) => {
Current_btn.value = item.value; emit('update:modelValue', item.value);
}; };
watch(
() => props.modelValue,
(v) => {
Current_btn.value = v;
}
);
watch(
() => Current_btn.value,
(v) => {
emit('update:modelValue', v);
}
);
</script> </script>
<style lang="less"> <style lang="less">
......
...@@ -33,7 +33,7 @@ const props = withDefaults( ...@@ -33,7 +33,7 @@ const props = withDefaults(
} }
); );
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
const SelectValue = ref(''); const SelectValue = ref(props.modelValue);
watch( watch(
() => SelectValue.value, () => SelectValue.value,
(v) => { (v) => {
......
...@@ -7,13 +7,14 @@ ...@@ -7,13 +7,14 @@
:columns="columns" :columns="columns"
hover hover
:loading="loading" :loading="loading"
></TTable> >
</TTable>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { Table as TTable } from "tdesign-vue-next"; import { Table as TTable } from 'tdesign-vue-next';
import { ref } from "vue"; import { ref } from 'vue';
const props = defineProps<{ const props = defineProps<{
columns: any[]; columns: any[];
list: any[]; list: any[];
...@@ -22,7 +23,7 @@ const loading = ref(false); ...@@ -22,7 +23,7 @@ const loading = ref(false);
</script> </script>
<style lang="less"> <style lang="less">
@import "@/style/variables.less"; @import '@/style/variables.less';
.reset-t-table { .reset-t-table {
background-color: transparent; background-color: transparent;
.t-table__content { .t-table__content {
......
...@@ -102,7 +102,7 @@ const Cur_pwd_type = ref<string>('private'); ...@@ -102,7 +102,7 @@ const Cur_pwd_type = ref<string>('private');
const numberInput = (e: string) => { const numberInput = (e: string) => {
const { type } = props; const { type } = props;
if (type == 'number') { if (type == 'number') {
input_value.value = e.replace(/[^\d.]/g, ''); input_value.value = e.replace(/[^\d]/g, '');
} }
// 提交输入事件 // 提交输入事件
emit('inputChange', input_value.value); emit('inputChange', input_value.value);
......
<template> <template>
<div
class="custom-loading-two"
:style="{
position: position,
}"
>
<div class="loading"> <div class="loading">
<div></div> <div></div>
<div></div> <div></div>
...@@ -9,9 +15,26 @@ ...@@ -9,9 +15,26 @@
<div></div> <div></div>
<div></div> <div></div>
</div> </div>
</div>
</template> </template>
<style> <script lang="ts" setup>
const props = withDefaults(
defineProps<{
position?: string;
}>(),
{
position: 'absolute',
}
);
</script>
<style lang="less">
.custom-loading-two {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.loading, .loading,
.loading > div { .loading > div {
position: relative; position: relative;
......
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
<div class="recommend-content"> <div class="recommend-content">
<template v-for="(item, index) in list" :key="index"> <template v-for="(item, index) in list" :key="index">
<CustomCard <CustomCard
:img="item.img" :img="item.image"
:title="item.title" :title="item.title"
:content="item.content" :content="item.description"
@click="toGenerate(item)" @click="toGenerate(item)"
></CustomCard> ></CustomCard>
</template> </template>
...@@ -17,20 +17,14 @@ ...@@ -17,20 +17,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { reactive } from 'vue'; import { reactive } from 'vue';
import CustomCard from '@/components/card.vue'; import CustomCard from '@/components/card.vue';
import { useRouter } from 'vue-router';
const props = defineProps<{ const props = defineProps<{
list: any[]; list: any[];
label: string; label: string;
}>(); }>();
const router = useRouter(); const emit = defineEmits(['change']);
const toGenerate = (item: any) => { const toGenerate = (item: any) => {
// 判断类型跳转页面 // 判断类型跳转页面
console.log(item); emit('change', item);
const url = router.resolve({
// path: '/CopywritingGeneration',
path: '/ImageGeneration',
});
window.open(url.href);
}; };
</script> </script>
......
...@@ -27,13 +27,13 @@ const route = useRoute(); ...@@ -27,13 +27,13 @@ const route = useRoute();
background: #181818; background: #181818;
.custom-content { .custom-content {
flex: 1; flex: 1;
max-height: calc(100vh - 60px); max-height: calc(100vh - 67px);
overflow: auto; overflow: auto;
.center-box { .center-box {
margin: 0 auto; margin: 0 auto;
// min-width: 90vw;
max-width: 1597px; max-width: 1597px;
box-sizing: border-box; box-sizing: border-box;
min-width: 90vw;
padding: 0 30px; padding: 0 30px;
height: 100%; height: 100%;
} }
......
<template> <template>
<div class="custom-copywriting-generation"> <div class="custom-copywriting-generation-page">
<img class="tip-box" :src="imgs.tips" alt="" /> <div class="custom-copywriting-generation" v-show="!loading">
<img class="tip-box" :src="res_img ?? imgs.tips" alt="" />
<div class="interaction-form"> <div class="interaction-form">
<div class="basic-info" v-for="item in AdminData.list" :key="item.name"> <div class="basic-info" v-for="item in AdminData.list" :key="item.name">
<div class="label">*{{ item.name }}</div> <div class="label">*{{ item.name }}</div>
...@@ -34,15 +35,19 @@ ...@@ -34,15 +35,19 @@
</div> </div>
<div class="confirm-box"> <div class="confirm-box">
<div>字符余额:0/50000</div> <div>字符余额:0/50000</div>
<CustomResetButton @click="onReset" width="20%">重置</CustomResetButton> <CustomResetButton @click="onReset" width="20%"
<CustomResetButton @click="onReset" width="50%" bold >重置</CustomResetButton
>生成图片</CustomResetButton >
<CustomResetButton @click="beforeSubmit" width="50%" bold
>生成文案</CustomResetButton
> >
</div> </div>
<div class="cust-line"></div> <div class="cust-line"></div>
<CustomGptMessage computed :list="MessageList.list"></CustomGptMessage> <CustomGptMessage computed :list="MessageList.list"></CustomGptMessage>
</div> </div>
</div> </div>
<CustomLoading v-show="loading"></CustomLoading>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
...@@ -51,87 +56,56 @@ import CustomTextArea from '@/components/custom/textarea.vue'; ...@@ -51,87 +56,56 @@ import CustomTextArea from '@/components/custom/textarea.vue';
import CustomInput from '@/components/custom/input/index.vue'; import CustomInput from '@/components/custom/input/index.vue';
import CustomResetButton from '@/components/custom/resetbutton.vue'; import CustomResetButton from '@/components/custom/resetbutton.vue';
import CustomGptMessage from '@/components/custom/gptmessage.vue'; import CustomGptMessage from '@/components/custom/gptmessage.vue';
import { onBeforeMount, reactive } from 'vue'; import { onBeforeMount, reactive, ref } from 'vue';
import { getScenesList, useSubmitConversation } from '@/utils/api/scenes';
import { useRoute } from 'vue-router';
import { show_message } from '@/utils/tdesign_tool';
import { FormExample, ConversationKey } from '@/utils/api/Task';
import CustomLoading from '@/components/custom/loading2.vue';
import { Validationrules } from '@/utils/tool';
const route = useRoute();
const id = route.query.id;
const imgs = { const imgs = {
tips: new URL('../../assets/img/tips.png', import.meta.url).href, tips: new URL('../../assets/img/tips.png', import.meta.url).href,
}; };
// 场景id
const scenario_id = ref();
// 场景示例图
const res_img = ref('');
const loading = ref(false);
// 后台配置的输入类型--input-select-textarea // 后台配置的输入类型--input-select-textarea
// type-1是input,2是select,3是长文本输入 // type-1是input,2是select,3是长文本输入
const AdminData = reactive({ const AdminData = reactive({
list: [], list: [],
}); });
// 获取本地存储的对话记录--先返回空数组
const getLocalList = () => {
return [];
let list = localStorage.getItem(ConversationKey);
if (list) {
return JSON.parse(list);
}
return [];
};
// gpt-消息列表 // gpt-消息列表
const MessageList = reactive({ const MessageList = reactive({
list: [ // 显示在页面的list
{ list: [],
user: 'user', // 存本地的完整对话列表
message: localList: getLocalList(),
'早上好,早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好,早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好',
},
{
user: 'user',
message: '',
},
],
}); });
// 获取后台配置的组件 // 获取后台配置的组件
const getAdminComponent = async () => { const getAdminComponent = async () => {
try { try {
// let res:any = await ddd(); loading.value = true;
// if(res.data){ console.log(JSON.stringify(FormExample));
// } let res: any = await getScenesList(id, 'id');
let list = [ if (res.code == 0) {
{ // 取id
name: '基础填写', scenario_id.value = res.data.id;
value: 'name', // 取图片
lists: [ res_img.value = res.data.example_image;
{ let list = res.data.form;
type: 'text',
name: 'url',
label: '链接',
value: null,
span: 24,
placeholder: '输入链接',
// component_type: 'input',
},
// 下拉选择
{
type: 'select',
options: [
{
label: '第一个',
value: 1,
},
{
label: '第二个',
value: 2,
},
],
// 可设置默认值
value: 1,
},
],
},
{
name: '详细描述',
value: '2',
lists: [
{
// 长文本输入框
type: 'textarea',
name: 'url',
label: '链接',
value: null,
span: 24,
maxRows: '5',
minRows: '5',
placeholder: '详细描述产品细节,生成的文案更加完美。',
maxlength: 100,
// component_type: 'input',
},
],
},
];
// 修改数据 // 修改数据
list.forEach((item: any) => { list.forEach((item: any) => {
item.lists.forEach((it: any) => { item.lists.forEach((it: any) => {
...@@ -150,16 +124,89 @@ const getAdminComponent = async () => { ...@@ -150,16 +124,89 @@ const getAdminComponent = async () => {
}); });
}); });
AdminData.list = list; AdminData.list = list;
console.log(AdminData.list); }
loading.value = false;
} catch (e) { } catch (e) {
console.log(e); console.log(e);
loading.value = false;
} }
}; };
// 重置 // 重置
const onReset = () => { const onReset = () => {
console.log('111'); // 遍历整个列表,清空value
AdminData.list.forEach((item: any) => {
item.lists.forEach((it: any) => {
if (it.value) {
it.value = '';
}
});
});
};
// 提交
const onSubmit = async (params: any) => {
try {
// 提交前先创建一个消息
MessageList.list.push({
message: '',
});
let res: any = await useSubmitConversation({
scenario_id: scenario_id.value,
// messages: MessageList.localList,
messages: [],
parameters: params,
});
if (res.code == 0) {
// 完整对话记录
MessageList.localList = res.data;
localStorage.setItem(
ConversationKey,
JSON.stringify(MessageList.localList)
);
// 要显示的内容--找到最后一条属于机器人的消息
let RobotList = MessageList.localList.filter(
(item: any) => item.role == 'assistant'
);
if (RobotList && RobotList.length) {
MessageList.list[MessageList.list.length - 1].message =
RobotList[RobotList.length - 1].content;
}
}
console.log(res);
// message列表存到本地
} catch (e) {
// 删除最后一个
MessageList.list.pop();
console.log(e);
}
};
// 提交前的校验
const beforeSubmit = () => {
let params: any = {};
// 遍历整个列表
for (let i = 0; i < AdminData.list.length; i++) {
let item = AdminData.list[i];
for (let j = 0; j < item.lists.length; j++) {
let it = item.lists[j];
// 校验规则
let message = Validationrules(it.rules, it.value);
if (message) {
// 提示错误信息
show_message(message);
return;
} else {
// 添加params
params[it.name] = it.value;
}
}
}
onSubmit(params);
}; };
onBeforeMount(async () => { onBeforeMount(async () => {
if (!id) {
show_message('禁止访问');
return;
}
// 获取组件列表 // 获取组件列表
await getAdminComponent(); await getAdminComponent();
}); });
...@@ -167,9 +214,12 @@ onBeforeMount(async () => { ...@@ -167,9 +214,12 @@ onBeforeMount(async () => {
<style lang="less"> <style lang="less">
@import '@/style/variables.less'; @import '@/style/variables.less';
.custom-copywriting-generation { .custom-copywriting-generation-page {
margin-top: @page-margin-top; position: relative;
height: 100%;
.custom-copywriting-generation {
display: flex; display: flex;
padding-top: @page-margin-top;
.tip-box { .tip-box {
width: 50%; width: 50%;
object-fit: contain; object-fit: contain;
...@@ -220,5 +270,6 @@ onBeforeMount(async () => { ...@@ -220,5 +270,6 @@ onBeforeMount(async () => {
margin-top: 20px; margin-top: 20px;
} }
} }
}
} }
</style> </style>
<template>
<div>
<TTable
class="reset-t-table"
row-key="index"
:data="RecordList.list"
:columns="columns"
:loading="loading"
>
<template #detail="{ row }">
<div class="detail-box" v-for="item in row.detail" :key="item.name">
<span>{{ item.label }}</span>
<span>{{ item.value }}</span>
</div>
</template>
<template #download="{ row }">
<span class="download-text"> 查看 </span>
</template>
</TTable>
<div class="custom-pagination">
<t-pagination
v-model="pageNum"
v-model:page-size="pageSize"
:total="total"
:pageSizeOptions="[]"
@current-change="pageChange"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import { Table as TTable } from 'tdesign-vue-next';
import CustomTTable from '@/components/custom/TTable.vue';
import { onBeforeMount, reactive, ref } from 'vue';
import { getGenerateRecords } from '@/utils/api/scenes';
import { Pagination as TPagination } from 'tdesign-vue-next';
import { TASKTYPE } from '@/utils/api/Task';
const RecordList = reactive({
// 绘图列表
list: [],
});
const loading = ref(false);
const pageNum = ref<number>(1);
const pageSize = ref<number>(10);
const total = ref<number>(0);
// 文案记录
const columns = [
{
title: '产品',
colKey: 'title',
},
{
title: '详细',
colKey: 'detail',
align: 'center',
},
{
title: '消耗字符数',
colKey: 'consumes_characters',
align: 'center',
},
{
title: '操作',
colKey: 'download',
align: 'right',
},
];
const pageChange = (value: number) => {
pageNum.value = value;
getLog();
};
const getLog = async () => {
try {
loading.value = true;
let res: any = await getGenerateRecords({
page: pageNum.value,
limit: pageSize.value,
type: TASKTYPE.CHAT,
});
if (res.code == 0) {
RecordList.list = res.data.data;
total.value = res.data.total;
}
console.log(res);
loading.value = false;
} catch (e) {
loading.value = false;
console.log(e);
}
};
onBeforeMount(() => {
getLog();
});
</script>
<style lang="less">
@import '@/style/variables.less';
.reset-t-table {
background-color: transparent;
min-height: 500px;
.t-table__content {
background-color: transparent;
}
thead {
tr {
background-color: transparent;
th {
border: none;
background: #000000;
color: white;
font-size: @font-size-16;
font-weight: 400;
}
& > :first-child {
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
}
& > :last-child {
border-top-right-radius: 8px;
border-bottom-right-radius: 8px;
}
}
}
tbody {
.t-table__empty-row {
background-color: transparent;
&:hover {
background-color: transparent;
}
.t-table__empty {
color: white;
}
}
tr {
background: transparent;
td {
color: #ffffff;
border-bottom: 1px solid #464646;
font-weight: 400;
font-size: @font-size-14;
.detail-box {
text-align: start;
}
.img {
width: 85px;
height: 85px;
border-radius: 8px;
}
.download-text {
font-weight: 400;
font-size: @font-size-14;
color: #00f9f9;
cursor: pointer;
}
}
}
}
}
.custom-pagination {
padding: 12px 0;
.t-pagination__total {
color: white;
}
.t-is-current {
background: #00f9f9;
border-radius: 2px;
border: none;
color: #000000;
}
.t-pagination__btn {
& > :nth-child(1) {
color: #c9cdd4;
}
}
}
</style>
<template> <template>
<CustomTTable <div>
class="mapping-record-table" <TTable
class="reset-t-table"
row-key="index"
:data="RecordList.list"
:columns="columns" :columns="columns"
:list="RecordList.list" :loading="loading"
></CustomTTable> >
<template #detail="{ row }">
<div class="detail-box" v-for="item in row.detail" :key="item.name">
<span>{{ item.label }}</span>
<span>{{ item.value }}</span>
</div>
</template>
<template #size="{ row }">
<div v-for="(item, index) in row.size" :key="item">
<span
>{{ item }}
<template v-if="index != 0">,</template>
</span>
</div>
</template>
<template #url="{ row }">
<img class="img" :src="row.url" alt="" />
</template>
<template #download="{ row }">
<span class="download-text"> 下载 </span>
</template>
</TTable>
<div class="custom-pagination">
<t-pagination
v-model="pageNum"
v-model:page-size="pageSize"
:total="total"
:pageSizeOptions="[]"
@current-change="pageChange"
/>
</div>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import CustomTTable from "@/components/custom/TTable.vue"; import { Table as TTable } from 'tdesign-vue-next';
import { reactive } from "vue"; import CustomTTable from '@/components/custom/TTable.vue';
import { onBeforeMount, reactive, ref } from 'vue';
import { getGenerateRecords } from '@/utils/api/scenes';
import { Pagination as TPagination } from 'tdesign-vue-next';
import { TASKTYPE } from '@/utils/api/Task';
const RecordList = reactive({ const RecordList = reactive({
// 绘图列表 // 绘图列表
list: [], list: [],
}); });
const loading = ref(false);
const pageNum = ref<number>(1);
const pageSize = ref<number>(10);
const total = ref<number>(0);
// 绘图记录 // 绘图记录
const columns = [ const columns = [
{ {
title: "产品", title: '产品',
colKey: "name", colKey: 'title',
}, },
{ {
title: "详细", title: '详细',
colKey: "packages", colKey: 'detail',
align: "center", align: 'center',
className: "n_quantity", width: '40%',
}, },
{ {
title: "尺寸", title: '尺寸',
colKey: "n_funds", colKey: 'size',
align: "center", align: 'center',
className: "n_funds",
}, },
{ {
title: "数量", title: '数量',
colKey: "operation", colKey: 'number',
align: "right", align: 'center',
}, },
{ {
title: "图片", title: '图片',
colKey: "img", colKey: 'url',
align: "center", align: 'center',
}, },
{ {
title: "下载", title: '下载',
colKey: "download", colKey: 'download',
align: "right", align: 'right',
}, },
]; ];
const pageChange = (value: number) => {
pageNum.value = value;
getLog();
};
const getLog = async () => {
try {
loading.value = true;
let res: any = await getGenerateRecords({
page: pageNum.value,
limit: pageSize.value,
type: TASKTYPE.PAINTING,
});
if (res.code == 0) {
RecordList.list = res.data.data;
total.value = res.data.total;
}
console.log(res);
loading.value = false;
} catch (e) {
loading.value = false;
console.log(e);
}
};
onBeforeMount(() => {
getLog();
});
</script> </script>
<style lang="less"> <style lang="less">
.mapping-record-table { @import '@/style/variables.less';
.reset-t-table {
background-color: transparent;
min-height: 500px;
.t-table__content {
background-color: transparent;
}
thead {
tr {
background-color: transparent;
th {
border: none;
background: #000000;
color: white;
font-size: @font-size-16;
font-weight: 400;
}
& > :first-child {
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
}
& > :last-child {
border-top-right-radius: 8px;
border-bottom-right-radius: 8px;
}
}
}
tbody {
.t-table__empty-row {
background-color: transparent;
&:hover {
background-color: transparent;
}
.t-table__empty {
color: white;
}
}
tr {
background: transparent;
td {
color: #ffffff;
border-bottom: 1px solid #464646;
font-weight: 400;
font-size: @font-size-14;
.detail-box {
text-align: start;
}
.img {
width: 85px;
height: 85px;
border-radius: 8px;
}
.download-text {
font-weight: 400;
font-size: @font-size-14;
color: #00f9f9;
cursor: pointer;
}
}
}
}
}
.custom-pagination {
padding: 12px 0;
.t-pagination__total {
color: white;
}
.t-is-current {
background: #00f9f9;
border-radius: 2px;
border: none;
color: #000000;
}
.t-pagination__btn {
& > :nth-child(1) {
color: #c9cdd4;
}
}
} }
</style> </style>
...@@ -17,10 +17,7 @@ ...@@ -17,10 +17,7 @@
</div> </div>
<template v-if="is_first"> <template v-if="is_first">
<div v-show="DefaultTable == 'text'"> <div v-show="DefaultTable == 'text'">
<CustomTTable <CopywritingRecord></CopywritingRecord>
:columns="columns2"
:list="RecordList.textlist"
></CustomTTable>
</div> </div>
</template> </template>
</div> </div>
...@@ -28,11 +25,12 @@ ...@@ -28,11 +25,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import CustomTTable from "@/components/custom/TTable.vue"; import CustomTTable from '@/components/custom/TTable.vue';
import MappingRecord from "./components/MappingRecord.vue"; import MappingRecord from './components/MappingRecord.vue';
import { reactive, ref } from "vue"; import CopywritingRecord from './components/CopywritingRecord.vue';
import { reactive, ref } from 'vue';
// 默认展示绘图记录 // 默认展示绘图记录
const DefaultTable = ref<"img" | "text">("img"); const DefaultTable = ref<'img' | 'text'>('img');
const is_first = ref(false); const is_first = ref(false);
const RecordList = reactive({ const RecordList = reactive({
...@@ -43,37 +41,37 @@ const RecordList = reactive({ ...@@ -43,37 +41,37 @@ const RecordList = reactive({
}); });
const label_list = [ const label_list = [
{ {
label: "绘图记录", label: '绘图记录',
value: "img", value: 'img',
}, },
{ {
label: "文案记录", label: '文案记录',
value: "text", value: 'text',
}, },
]; ];
// 文案记录 // 文案记录
const columns2 = [ const columns2 = [
{ {
title: "账号", title: '账号',
colKey: "name", colKey: 'name',
}, },
{ {
title: "套餐", title: '套餐',
colKey: "packages", colKey: 'packages',
align: "center", align: 'center',
className: "n_quantity", className: 'n_quantity',
}, },
{ {
title: "状况", title: '状况',
colKey: "n_funds", colKey: 'n_funds',
align: "center", align: 'center',
className: "n_funds", className: 'n_funds',
}, },
{ {
title: "到期时间", title: '到期时间',
colKey: "operation", colKey: 'operation',
align: "right", align: 'right',
}, },
]; ];
const changeTable = (item: any) => { const changeTable = (item: any) => {
...@@ -83,9 +81,9 @@ const changeTable = (item: any) => { ...@@ -83,9 +81,9 @@ const changeTable = (item: any) => {
</script> </script>
<style lang="less"> <style lang="less">
@import "@/style/variables.less"; @import '@/style/variables.less';
.custom-creation-record { .custom-creation-record {
margin-top: @page-margin-top; padding-top: @page-margin-top;
.label-box { .label-box {
font-weight: 600; font-weight: 600;
font-size: @font-size-18; font-size: @font-size-18;
......
<template> <template>
<div class="custom-product-detail"> <div class="custom-product-detail">
<ModuleCard label="亚马逊工具" :list="ProductList.list"></ModuleCard> <ModuleCard
<Animation v-show="loading"></Animation> :label="parent_title"
:list="ProductList.list"
@change="onChange"
></ModuleCard>
<CustomLoading v-show="loading"></CustomLoading>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useRoute } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { onMounted, reactive, ref } from 'vue'; import { onMounted, reactive, ref } from 'vue';
import { show_message } from '@/utils/tdesign_tool'; import { show_message } from '@/utils/tdesign_tool';
import ModuleCard from '@/components/modulecard.vue'; import ModuleCard from '@/components/modulecard.vue';
import Animation from '@/components/Animation.vue'; import Animation from '@/components/Animation.vue';
import { getScenesList } from '@/utils/api/scenes';
import CustomLoading from '@/components/custom/loading2.vue';
import { TASKTYPE } from '@/utils/api/Task';
// 会带一个id // 会带一个id
const route = useRoute(); const route = useRoute();
const params_id = route.params.id; const router = useRouter();
const params_id = route.query.id;
const loading = ref(false); const loading = ref(false);
// 产品大标题
const parent_title = ref('');
// 产品列表 // 产品列表
const ProductList = reactive({ const ProductList = reactive({
list: [], list: [],
}); });
// 这里跳转传id
const onChange = (item: any) => {
console.log(item);
let path = '';
if (item.type == TASKTYPE.CHAT) {
// 文案
path = '/CopywritingGeneration';
} else if (item.type == TASKTYPE.PAINTING) {
// 绘图
path = '/ImageGeneration';
}
if (path) {
const url = router.resolve({
path: path,
query: {
id: item.id,
},
});
window.open(url.href);
}
};
// 获取当前分类所有产品 // 获取当前分类所有产品
const getallproduct = () => { const getallproduct = async () => {
try { try {
if (!params_id) { if (typeof params_id == 'undefined') {
show_message('没有类目id'); show_message('没有类目id');
return; return;
} }
// let res: any = await ddd(); loading.value = true;
// console.log(res); let res: any = await getScenesList(params_id, 'parent');
if (res.code == 0) {
parent_title.value = res.data[0].parent_title;
ProductList.list = res.data;
}
console.log(res);
loading.value = false;
} catch (e) { } catch (e) {
console.log(e); console.log(e);
loading.value = false;
} }
}; };
onMounted(() => { onMounted(() => {
// 获取数据 // 获取数据
getallproduct(); getallproduct();
// 假数据生成
for (let i = 0; i < 20; i++) {
ProductList.list.push({
img: new URL('../../assets/img/clothes.jpeg', import.meta.url).href,
title: '亚马逊工具',
content:
'你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好',
});
}
}); });
</script> </script>
<style lang="less"> <style lang="less">
.custom-product-detail { .custom-product-detail {
padding-bottom: 40px;
box-sizing: border-box; box-sizing: border-box;
height: 100%;
position: relative;
.custom-recommend {
margin-top: 0;
padding-top: 30px;
}
} }
</style> </style>
...@@ -94,6 +94,9 @@ const goDetail = (item: any) => { ...@@ -94,6 +94,9 @@ const goDetail = (item: any) => {
console.log(item); console.log(item);
const url = router.resolve({ const url = router.resolve({
path: 'Detail', path: 'Detail',
query: {
id: item.id,
},
}); });
window.open(url.href); window.open(url.href);
}; };
......
@import '@/style/variables.less'; @import '@/style/variables.less';
.swiper-box { .swiper-box {
margin: @page-margin-top 0 30px 0; margin: @page-margin-top 0 30px 0;
margin-top: 0;
padding-top: @page-margin-top;
} }
...@@ -2,27 +2,48 @@ import { defineComponent, onMounted, reactive } from 'vue'; ...@@ -2,27 +2,48 @@ import { defineComponent, onMounted, reactive } from 'vue';
import HomeSwiper from './components/HomeSwiper/swiper.vue'; import HomeSwiper from './components/HomeSwiper/swiper.vue';
import './index.less'; import './index.less';
import ModuleCard from '@/components/modulecard.vue'; import ModuleCard from '@/components/modulecard.vue';
import { getScenariosRecommend } from '@/utils/api/scenes';
import { useRouter } from 'vue-router';
export default defineComponent({ export default defineComponent({
setup() { setup() {
const router = useRouter();
const CardList = reactive({ const CardList = reactive({
list: [] as any, list: [] as any,
}); });
onMounted(() => { const getRecommend = async () => {
// 创建测试列表 try {
for (let i = 0; i < 21; i++) { let res: any = await getScenariosRecommend();
CardList.list.push({ if (res.code == 0) {
img: new URL('../../assets/img/clothes.jpeg', import.meta.url).href, CardList.list = res.data;
title: '亚马逊产品文案生成', }
content: console.log(res.data);
'1111121111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111', } catch (e) {
console.log(e);
}
};
const onChange = (item: any) => {
if (item.type == 0) {
// 文案生成
const url = router.resolve({
path: '/CopywritingGeneration',
// path: '/ImageGeneration',
}); });
window.open(url.href);
} }
};
onMounted(() => {
// 获取推荐数据
getRecommend();
}); });
return () => ( return () => (
<div> <div>
<div class="swiper-box"> <div class="swiper-box">
<HomeSwiper></HomeSwiper> <HomeSwiper></HomeSwiper>
<ModuleCard label="推荐工具" list={CardList.list}></ModuleCard> <ModuleCard
label="推荐工具"
list={CardList.list}
onChange={onChange}
></ModuleCard>
</div> </div>
</div> </div>
); );
......
@import '@/style/variables.less'; @import '@/style/variables.less';
.empty-box, .img-res-status {
.loading-box { display: flex;
justify-content: center;
align-items: center;
.empty-box,
.loading-box {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
...@@ -9,19 +13,17 @@ ...@@ -9,19 +13,17 @@
font-size: @font-size-16; font-size: @font-size-16;
color: #888fa1; color: #888fa1;
} }
} }
.loading-box { .load-success-box {
}
.load-success-box {
padding: 0 20px; padding: 0 20px;
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
.result-img-box { .result-img-box {
padding-top: 100px; padding: 100px 0 20px 0;
display: flex; display: flex;
justify-content: space-between; justify-content: center;
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
row-gap: 20px; row-gap: 20px;
...@@ -49,4 +51,5 @@ ...@@ -49,4 +51,5 @@
} }
} }
} }
}
} }
...@@ -36,7 +36,7 @@ export default defineComponent({ ...@@ -36,7 +36,7 @@ export default defineComponent({
{props.list {props.list
? props.list.map((item: any) => ( ? props.list.map((item: any) => (
<div class="result-img"> <div class="result-img">
<img class="img" src={item.img} alt="" /> <img class="img" src={item} alt="" />
</div> </div>
)) ))
: ''} : ''}
...@@ -50,13 +50,18 @@ export default defineComponent({ ...@@ -50,13 +50,18 @@ export default defineComponent({
// 判断当前展示的模块 // 判断当前展示的模块
const CurrentModule = () => { const CurrentModule = () => {
if (true) { // return LoadSuccessHTML();
// return EmptyHTML(); const { status } = props;
// return LoadingHTML(); if (status == '') {
// 未开始
return EmptyHTML();
} else if (status == 'loading') {
return LoadingHTML();
} else {
return LoadSuccessHTML(); return LoadSuccessHTML();
} }
}; };
return () => <div>{CurrentModule()}</div>; return () => <div class="img-res-status">{CurrentModule()}</div>;
}, },
}); });
...@@ -73,6 +73,11 @@ ...@@ -73,6 +73,11 @@
font-size: @font-size-15; font-size: @font-size-15;
color: #8b8b8b; color: #8b8b8b;
} }
.custom-t-progress {
.t-progress__info {
color: #00f9f9;
}
}
} }
.custom-UploadSuccess-stauts { .custom-UploadSuccess-stauts {
display: flex; display: flex;
......
import { computed, defineComponent, reactive, ref } from 'vue'; import { defineComponent, reactive, ref, watch } from 'vue';
import './index.less'; import './index.less';
import UploadTip from '@/assets/svg/upload/uploadTip2.svg'; import UploadTip from '@/assets/svg/upload/uploadTip2.svg';
import { import {
...@@ -17,16 +17,12 @@ import { v4 } from 'uuid'; ...@@ -17,16 +17,12 @@ import { v4 } from 'uuid';
export default defineComponent({ export default defineComponent({
props: { props: {
modelValue: String, modelValue: String,
config: Object as any,
num: Number,
}, },
emits: ['update:modelValue'], emits: ['update:modelValue'],
setup(props, { emit }) { setup(props, { emit }) {
const store = useStore(); const store = useStore();
// 后台配置的地址
const adminConfigUrl = computed(() => store.getters['user/getadminConfig']);
// 上传策略
const uploadStrategy = computed(
() => store.getters['user/getuploadStrategy']
);
const files = ref([]); const files = ref([]);
// 文件地址 // 文件地址
const Curfile = reactive({ const Curfile = reactive({
...@@ -53,7 +49,7 @@ export default defineComponent({ ...@@ -53,7 +49,7 @@ export default defineComponent({
}; };
const beforeUpload = (file: File) => { const beforeUpload = (file: File) => {
try { try {
let config = uploadStrategy.value.config; const { config } = props;
let config_len = Object.keys(config).length; let config_len = Object.keys(config).length;
if (!config_len) { if (!config_len) {
show_message('无法上传,请尝试刷新页面'); show_message('无法上传,请尝试刷新页面');
...@@ -89,45 +85,6 @@ export default defineComponent({ ...@@ -89,45 +85,6 @@ export default defineComponent({
emit('update:modelValue', Curfile.url); emit('update:modelValue', Curfile.url);
MessagePlugin.warning('上传失败'); MessagePlugin.warning('上传失败');
}; };
// 内网上传-Intranet
const IntranetUpload = (file: any) => {
openpercentage();
return new Promise((resolve) => {
let uuid = v4();
// 上传中状态
Curfile.status = 1;
let url = '';
if (import.meta.env.MODE == 'production') {
// 线上地址使用完整url
url = adminConfigUrl.value + 'video/' + uuid + '.png';
// url = `http://192.168.1.19:5000/video/` + uuid + '.png';
} else if (import.meta.env.MODE == 'app') {
// app
url = '/video/' + uuid + '.png';
} else {
// 本地
url = '/video/' + uuid + '.png';
}
setTimeout(() => {
request.put(url, file[0].raw).then((res: any) => {
// resolve 参数为关键代码
if (res == 200) {
let url = adminConfigUrl.value + 'video/' + uuid + '.png';
UploadSuccessCallback(uuid, url);
//
Curfile.uploadStatus = true;
resolve({
status: 'success',
response: { url: Curfile.url },
});
} else {
UploadErrorCallback();
Curfile.uploadStatus = false;
}
});
}, 1000);
});
};
// 外网上传-func // 外网上传-func
const ExtranetUpload = (file: any) => { const ExtranetUpload = (file: any) => {
openpercentage(); openpercentage();
...@@ -136,7 +93,7 @@ export default defineComponent({ ...@@ -136,7 +93,7 @@ export default defineComponent({
// 上传中状态 // 上传中状态
Curfile.status = 1; Curfile.status = 1;
let url = ''; let url = '';
const { config } = uploadStrategy.value; const { config } = props;
url = 'https://' + config.host; url = 'https://' + config.host;
setTimeout(() => { setTimeout(() => {
let formData = new FormData(); let formData = new FormData();
...@@ -216,9 +173,11 @@ export default defineComponent({ ...@@ -216,9 +173,11 @@ export default defineComponent({
return ( return (
<div class="custom-uploading-stauts"> <div class="custom-uploading-stauts">
<TProgress <TProgress
class="custom-t-progress"
theme="circle" theme="circle"
percentage={percentage.value} percentage={percentage.value}
size={'small'} size={'small'}
color={'#00f9f9'}
/> />
<div class="uploading-title">正在上传</div> <div class="uploading-title">正在上传</div>
</div> </div>
...@@ -243,6 +202,12 @@ export default defineComponent({ ...@@ -243,6 +202,12 @@ export default defineComponent({
return UploadSuccess(); return UploadSuccess();
} }
}; };
watch(
() => props.num,
(v) => {
Curfile.status = 0;
}
);
return () => ( return () => (
<div class="custom-real-upload custom-upload-line"> <div class="custom-real-upload custom-upload-line">
<div class="real-upload-content"> <div class="real-upload-content">
......
<template> <template>
<div class="custom-copywriting-generation"> <div class="custom-copywriting-generation">
<div class="generation-box" v-show="!loading">
<div class="interaction-form"> <div class="interaction-form">
<div class="basic-info" v-for="item in AdminData.list" :key="item.name"> <div class="basic-info" v-for="item in AdminData.list" :key="item.name">
<div class="label">*{{ item.name }}</div> <div class="label">*{{ item.name }}</div>
...@@ -27,14 +28,18 @@ ...@@ -27,14 +28,18 @@
:maxlength="it.maxlength" :maxlength="it.maxlength"
></CustomTextArea> ></CustomTextArea>
</template> </template>
<template v-else-if="it.component_type == 'radiogroup_size'"> <template v-else-if="it.component_type == 'radio_group_size'">
<ImgSizeRadioGroupVue <ImgSizeRadioGroupVue
:list="it.options" :list="it.options"
v-model="it.value" v-model="it.value"
></ImgSizeRadioGroupVue> ></ImgSizeRadioGroupVue>
</template> </template>
<template v-else-if="it.component_type == 'upload'"> <template v-else-if="it.component_type == 'upload'">
<CustomUpload v-model="it.value"></CustomUpload> <CustomUpload
v-model="it.value"
:config="StrategyConfig"
:num="AdminData.reset_num"
></CustomUpload>
</template> </template>
<template v-else-if="it.component_type == 'custom-number'"> <template v-else-if="it.component_type == 'custom-number'">
<CustomInput <CustomInput
...@@ -49,38 +54,31 @@ ...@@ -49,38 +54,31 @@
</div> </div>
<div class="confirm-box"> <div class="confirm-box">
<div class="pay-num"> <div class="pay-num">
<div>余额:0/5000</div> <!-- <div>余额:0/5000</div>
<div>实付:100</div> <div>实付:100</div> -->
</div> </div>
<CustomResetButton @click="onReset" width="20%">重置</CustomResetButton> <CustomResetButton @click="onReset" width="20%"
<CustomResetButton @click="onReset" width="50%" bold >重置</CustomResetButton
>
<CustomResetButton @click="beforeSubmit" width="50%" bold
>生成图片</CustomResetButton >生成图片</CustomResetButton
> >
</div> </div>
</div> </div>
<div class="generate-result"> <div class="generate-result">
<CustomGenerateResult <CustomGenerateResult
status="success" :status="AdminData.status"
:list="[ :list="AdminData.callback_list"
{
img: 'data:image/jpeg;base64,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',
},
{
img: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxAPEBAQEBAPDw8QEA4NDQ8PDRANDw8OFRIWFhURFRUYHSogGBolGxUTITEhJSorLi4wFx8zODUsNygtLjcBCgoKDg0OGxAQGC0lHh0rKy0tKy0rLTIrLSstLS0tLSstLy03NS03Ky0tLS0tLjArKy0rLSsrLS0tNystLS0tLf/AABEIAKgBLAMBIgACEQEDEQH/xAAbAAEAAwADAQAAAAAAAAAAAAAAAQIDBAUHBv/EADIQAAIBAwQBAwIEBgIDAAAAAAABAgMEEQUSITFRBhNBYXEUIjKRByNCgaHRgrFUY3L/xAAaAQEBAQEBAQEAAAAAAAAAAAAAAQMCBAUG/8QAIxEBAAICAgEEAwEAAAAAAAAAAAECAxESIQQTMVFhMkGBFP/aAAwDAQACEQMRAD8A8bAB084AAJSNSlNF2ATyUmi6WCJrgCKZcpTJm/8AsBJfJYhMkClP5Jn0RT+SZ9AIdFisOic8gU28/wCTQgZ5AifRSK5Lz6IpoCzCYaCQGc1yQXqIoBqjOfZojOfYE0y0+itMtPoBBcETZMHwROOQIhIuysYlmwK0y5SmTNgGucliEyQMSCSAAAAAADSLREpeCgAspMvuRkANIMibKAC8JFtyMgBeDJm+DMAaRksESlyigA13IrGXZQAaTfBKaMgBeUvAjIoANW0ZkADVSRSb5KgC8GTKSwZgCYvBopoyAGrkikpZKgC8GTNmYAvCRbcjIASQAAAAAAAAAAAAAAAAAABOCcAVBfAwE2oC2CMBUAlo09r8u7K7xjK3ft4+pUmdMgWUCMEXaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALJEIsEkJwSkWSK5mVMDBqojaE5MsEGjiVaCxKjRGS7RRkdQ5VC42xlHEWpbctxTksPPD+C8bKcoSqqL9uLUZTx+VSecJv+zOHE5dGpLG1N4eMrPDZYZXiY7q4solTsdQ06pR2qpFwcoqpHPGYvpnAkhMad0vFo3EqAAjsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWRZIhFkVzKyRrGJSCOVRgVle2kRpCVI+p9N6C7qTSaikt0m/hFPUGiu2m4PD4TTXTTNPTnjt87/fj9X0t9vlJQM5I51WmcacTOYe+l9uO0ZyN3EymiNqyojkUuzGETkUo8hLy5tWnUqQ3tSlFYjueWl4WTrakTvI31VUHQy/alJTcccOWMZOmqrk6swwTbuJ/jjsgtIqcPWAAAAAAAAAAAAAAAAAAAAAAAAAAAAALouZxNEVzLWmcugcODOVRZYefJD6v05fVKUl7bw3x1nP0wdp6jsq7/AJtZfq4zxx9OOj5nTLpwkmu0019z7Kd/Vv4qnGMePzPHGX5eXx2eqkxNdPznl1tizxkiI1+5fB16XJwqtM+j1XT5UpOMlhrs6erSMbVfWwZotETEutcSHA5UqRT2jPT2RdxVA5FCmXjROy0+Mac4SqQ3QTTlHrK8FiHGXLqOmlbUl+GVD2oJqe/3cfnfH6c+D52s+TvfUN1SqVZSow9uDxiGeuDoahbz258SsceWtb719sJFS0ipm+hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlM0iZFosqTDZG9ORx0y8WGVo27KhUPo9B1uVvLMcPKw0+mj5GnM5VKsaVtMdvB5Hj1yVmto6ff2du9RqTlKSjxl4WfokkdJqGlOFWVOP5mm48c5OustTnT5jJxflPDO00bXPZqqpJbu8pvyvJtyrb3fK9DNhmZp3ER1Dqq1lKLw1h9YwZysmmso7u/1qNW495xWMp7e1heR6j12FxKMowUNqx3ls5mte+29M2eZrE0947+nEvNEqW8IVJxxGfMeU/r/AGGt6vSq0aUIUo05U44lJYzPrn/H+ThX+tVasIwnNyjDiKb6R01Wsc2tEfi9GHxr31bL7xM+yleocWTLzkYzZjL61K6VZABGoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEkAC8ZGkZGJKkVzMOQpGkahxlIupBnNXMjWLqucFSJ3F2znHDnOuZyrnF3kOQ2RihrOqZSkVcjOUiNa0TKRQZII0iAABQAAAAAAAAAAAAAAAAAAAAAAAAAAel0NFprT7advptreW1WwnXv9Qq3St61ve5lvgqrl/L2YjiO17s/c5EtFsVoSru3ozrrTPxMvbpSV578q7hC59zel7Ucfmjhvo8t+nw2m18Nrpjc/L6x38ePsTTvl9PUvVvpKFR6Xb2ttCirqVlCdenZSpvM6G6o3ce41PjfJx2LG3t4OB/EDT7S1q2l/Y0bGrayncWlShCrG8tXWpSlslU2S/VOlOE9ueHH5+fPd78vjrl8EZ4x8d4+MjRyema5p1o9R1q3jaWtKlY6XeTtlSpuL95RoyjVk88zW+STWODja1pllDTJ6rCjRSvLSztbWiuI0NRUpxu5wj/TtjRyn5mzzzc/L5757Gf28fBdHN6J/FDR6VrRtHQtadGnONu5VYWUqW+crdSa/Ee41Uy9z27FjHbwZXPpyhjQKtSk6Njc0LSGo3UU4QdadWWVOf8ATJxSWfhc/B8C5N9tv7tshyeMZePGeP2Gk5R8PTfVXpzdRUZWFtp15PU4WOmQt5Nfi7aeVvnHdLdh7H7nGcnM9UemLGNzpUrWNvK3Wo2+k38aVaFdVH7kMTq7W9spRVVNPno8m3Ph5eUkk89JdJeApPy/Pfz5GjcfD1zUfT9srmxVext7OrU1t2dO3p5jG80vLxWnScnjnas8Z3fYt6c9N6dcQs6dWnThcVNUv/blJfy69C3rx32k/vTlJx+tPHzz5Dl5zl5855/cZfl8PK56fkml5R8PXfSfpu0qwTha2tzvvNQo39SvGdX8FTp1EqFKKjOLpKUHKXu8424+UfE+lNEo3Oq+xKcJWVCtcV69VzSpOyoSlLc5Sf6JJRWX8TyfMefqsP6rw/IT/wBP7eC6TlD1dentPp6lWrVKFKpptXR6+pQp0Kka1OlUp7I1oUakXhyjKM2mn/WjHVPTFlQnKnCNGvCPpq8vqVeK4rV41Je1df8A3t2nl25+X8rvz2Mvy+sd/Hgml5fT2nSPRmm1ZaVcThSUIWdjHULf/wAm4uoU429Rx+cznWbf/qR1PpH0lZ1YXv4lW8J3l5d2Gme7Wp0p0VSVTFahGTzN+7KjDC8Hlm5+X8fL+Ohl8cvjrnoujk+00726ej30qtjZyurW5o2Sq1KMnWXvOrvcnu5nFrEX8bV2d5aaBaur/KtaFxdx0PTbu0sqnFK5uqkV71WUcr3JJc7c8nl+X5fPL57Yz08vKxh55WOsDRyetUtC0yNzdfi7anSUdGo3V/b0JOasLydVRqOly9slFqW3Lxn6nyP8SdGo2NxaUKPtNLTbOdWpS/RXrN1FKv8A8tqZ8nnvvnvnv7jP+l9vA0k26QAA5AAAAAAAADSn0ZmlPoCwYlHJSUcAWh0SRDoSjkCWVp9FZRwWp9AWBEo5KSjgC0OixWn0TKOQJfRnGOSZRwWh0BKQaKzZVTAmUcF0UcygG2AEZz7Aso5bLFKbLtAGjOSwWafwyjYEAAAAAAAAAAAAAAAAAACWXp9AATLPwVeQALQ6E5YAAo5ZLU+iQAm8FHLJAAvT6Jln4AAq8loPgABOOSFDyAAlFIzAA2RnPsABGOS3K+oAFkUqAAUAAAAAAAAAAAAAAAAAAH//2Q==',
},
{
img: 'data:image/jpeg;base64,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',
},
{
img: 'data:image/jpeg;base64,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',
},
]"
></CustomGenerateResult> ></CustomGenerateResult>
</div> </div>
</div> </div>
<CustomLoading v-show="loading"></CustomLoading>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import CustomLoading from '@/components/custom/loading2.vue';
import CustomSelect from '@/components/custom/Select.vue'; import CustomSelect from '@/components/custom/Select.vue';
import CustomTextArea from '@/components/custom/textarea.vue'; import CustomTextArea from '@/components/custom/textarea.vue';
import CustomInput from '@/components/custom/input/index.vue'; import CustomInput from '@/components/custom/input/index.vue';
...@@ -88,152 +86,60 @@ import CustomResetButton from '@/components/custom/resetbutton.vue'; ...@@ -88,152 +86,60 @@ import CustomResetButton from '@/components/custom/resetbutton.vue';
import CustomGptMessage from '@/components/custom/gptmessage.vue'; import CustomGptMessage from '@/components/custom/gptmessage.vue';
import CustomGenerateResult from './components/GenerateResult'; import CustomGenerateResult from './components/GenerateResult';
import CustomUpload from './components/upload'; import CustomUpload from './components/upload';
import { onBeforeMount, reactive } from 'vue'; import { onBeforeMount, onBeforeUnmount, reactive, ref, watch } from 'vue';
import ImgSizeRadioGroupVue from '@/components/custom/ImgSizeRadioGroup.vue'; import ImgSizeRadioGroupVue from '@/components/custom/ImgSizeRadioGroup.vue';
import CustomRadioGroup from '@/components/custom/RadioGroup.vue'; import CustomRadioGroup from '@/components/custom/RadioGroup.vue';
import {
getScenesList,
useUploadStrategy,
useSubmitConversationImage,
useImageCallback,
} from '@/utils/api/scenes';
import { FormExample2, CustomNum } from '@/utils/api/Task';
import { Validationrules } from '@/utils/tool';
import { useRoute } from 'vue-router';
import { show_message } from '@/utils/tdesign_tool';
const imgs = { const imgs = {
tips: new URL('../../assets/img/tips.png', import.meta.url).href, tips: new URL('../../assets/img/tips.png', import.meta.url).href,
}; };
// 后台配置的输入类型--input-select-textarea //
// type-1是input,2是select,3是长文本输入 const loading = ref(false);
const route = useRoute();
const id = route.query.id;
// 定时器
let IntervalImg: any = null;
// 上传策略状态
const StrategyStatus = ref(false);
// 上传策略
const StrategyConfig = ref({});
const AdminData = reactive({ const AdminData = reactive({
list: [], list: [],
}); // 回调图片列表
// gpt-消息列表 callback_list: [
const MessageList = reactive({ 'http://ai-gpt.test/admin/images/155bf4db0b486817071b3fe4733efb94.jpg',
list: [ 'http://ai-gpt.test/admin/images/155bf4db0b486817071b3fe4733efb94.jpg',
{ 'http://ai-gpt.test/admin/images/155bf4db0b486817071b3fe4733efb94.jpg',
user: 'user', 'http://ai-gpt.test/admin/images/155bf4db0b486817071b3fe4733efb94.jpg',
message:
'早上好,早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好,早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好早上好',
},
{
user: 'user',
message: '',
},
], ],
/**
* loading-生成中
* success-结束
*/
status: '',
reset_num: 1,
}); });
const scenario_id = ref();
// 获取后台配置的组件 // 获取后台配置的组件
const getAdminComponent = async () => { const getAdminComponent = async () => {
try { try {
// let res:any = await ddd(); loading.value = true;
// if(res.data){ console.log(JSON.stringify(FormExample2));
// } let res: any = await getScenesList(id, 'id');
let list = [ if (res.code == 0) {
{ // 取id
name: '基础填写', scenario_id.value = res.data.id;
value: 'name', let list = res.data.form;
lists: [ list.push(CustomNum);
{
type: 'text',
name: 'url',
label: '链接',
value: null,
span: 24,
placeholder: '输入链接',
// component_type: 'input',
},
// 下拉选择
{
type: 'select',
options: [
{
label: '第一个',
value: 1,
},
{
label: '第二个',
value: 2,
},
],
// 可设置默认值
value: 1,
},
],
},
{
name: '详细描述',
value: '2',
lists: [
{
// 长文本输入框
type: 'textarea',
name: 'url',
label: '链接',
value: null,
span: 24,
maxRows: '5',
minRows: '5',
placeholder: '详细描述产品细节,生成的文案更加完美。',
maxlength: 100,
// component_type: 'input',
},
],
},
// 图片尺寸
{
name: '图片尺寸',
value: '3',
lists: [
{
// 单选按钮组
type: 'radiogroup_size',
name: 'img_size',
value: '1600x1600',
span: 24,
placeholder: '详细描述产品细节,生成的文案更加完美。',
// component_type: 'input',
options: [
{
size: '1600x1600',
label: '亚马逊产品图尺寸',
value: '1600x1600',
},
{
size: '600x180',
label: '亚马逊a+主图',
value: '600x180',
},
{
type: 'custom',
label: '自定义',
value: '',
value1: '',
value2: '',
},
],
},
],
},
// 上传图片
{
name: '上传图片',
value: '4',
lists: [
{
// 单选按钮组
type: 'upload',
name: 'upload',
value: null,
span: 24,
},
],
},
// 生成数量
{
name: '生成数量',
value: '5',
lists: [
{
type: 'custom-number',
name: 'num',
value: null,
span: 24,
placeholder: '自定义',
},
],
},
];
// 修改数据 // 修改数据
list.forEach((item: any) => { list.forEach((item: any) => {
item.lists.forEach((it: any) => { item.lists.forEach((it: any) => {
...@@ -244,12 +150,14 @@ const getAdminComponent = async () => { ...@@ -244,12 +150,14 @@ const getAdminComponent = async () => {
} else if (it.type == 'textarea') { } else if (it.type == 'textarea') {
// 多行文本输入框 // 多行文本输入框
it.component_type = 'textarea'; it.component_type = 'textarea';
} else if (it.type == 'radiogroup_size') { } else if (it.type == 'radio_group_size') {
// 单选按钮 // 单选按钮
it.component_type = 'radiogroup_size'; it.component_type = 'radio_group_size';
} else if (it.type == 'radiogroup') { } else if (it.type == 'radiogroup') {
it.component_type = 'radiogroup'; it.component_type = 'radiogroup';
} else if (it.type == 'upload') { } else if (it.type == 'upload') {
// 获取上传策略
StrategyStatus.value = true;
// 上传 // 上传
it.component_type = 'upload'; it.component_type = 'upload';
} else if (it.type == 'custom-number') { } else if (it.type == 'custom-number') {
...@@ -259,32 +167,172 @@ const getAdminComponent = async () => { ...@@ -259,32 +167,172 @@ const getAdminComponent = async () => {
if (!it.value) { if (!it.value) {
it.value = ''; it.value = '';
} }
// 记得要push一个生成数量
}); });
}); });
AdminData.list = list; AdminData.list = list;
console.log(AdminData.list); }
loading.value = false;
} catch (e) {
console.log(e);
loading.value = false;
}
};
const getStrategy = async () => {
try {
let res: any = await useUploadStrategy();
if (res.code == 0) {
StrategyConfig.value = res.data;
}
} catch (e) {
console.log(e);
}
};
watch(
() => StrategyStatus.value,
(v) => {
if (v) {
// 获取上传策略
getStrategy();
}
}
);
// 获取图片回调
const ImageCallback = async (uuid: string) => {
try {
let res: any = await useImageCallback({
// key: uuid,
key: 'generate_image:beacfaacc2fb819c17bb92e2495deee7',
});
if (res.code == 0) {
if (res.data.image) {
AdminData.status = 'success';
if (res.data.image.length > AdminData.callback_list.length) {
AdminData.callback_list = res.data.image;
}
if (res.data.image.length == 4) {
// 关闭定时器
closeInterval();
}
}
}
console.log(res);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
}; };
// 开启定时器
const openInterval = (uuid: string) => {
IntervalImg = window.setInterval(() => {
// 请求接口
ImageCallback(uuid);
}, 2000);
};
// 关闭定时器
const closeInterval = () => {
if (IntervalImg) {
window.clearInterval(IntervalImg);
clearInterval(IntervalImg);
IntervalImg = null;
}
};
// 重置 // 重置
const onReset = () => { const onReset = () => {
console.log('111'); // 遍历整个列表,清空value
AdminData.list.forEach((item: any) => {
item.lists.forEach((it: any) => {
if (it.value) {
it.value = '';
}
});
});
// 恢复默认状态
AdminData.status = '';
AdminData.callback_list = [];
// 初始化图片状态
AdminData.reset_num += 1;
};
// 提交--ai图片任务
const onSubmit = async (params: any) => {
try {
let res: any = await useSubmitConversationImage({
scenario_id: scenario_id.value,
parameters: params,
});
if (res.code == 0) {
// 开启定时器
openInterval(res.data.uuid);
AdminData.status = 'loading';
}
console.log(res);
} catch (e) {
console.log(e);
}
};
// 提交前的校验
const beforeSubmit = () => {
let params: any = {};
// 遍历整个列表
for (let i = 0; i < AdminData.list.length; i++) {
let item = AdminData.list[i];
for (let j = 0; j < item.lists.length; j++) {
let it = item.lists[j];
// 校验规则
let message = Validationrules(it.rules, it.value, it.name);
if (message) {
// 提示错误信息
show_message(message);
return;
} else {
// 类型为图片尺寸且当前选择的是自定义
if (it.type == 'radio_group_size' && it.value == '') {
// 找到options列表里的自定义模块
let customObj = it.options.find((opt: any) => opt.type == 'custom');
if (customObj) {
if (customObj.value1 && customObj.value2) {
params[it.name] = customObj.value1 + 'x' + customObj.value2;
} else {
show_message('自定义图片尺寸未填写完整');
return;
}
}
} else if (it.name == 'number') {
// 转换为number类型
params[it.name] = parseFloat(it.value + '');
} else {
// 添加params
params[it.name] = it.value;
}
}
}
}
console.log(params);
onSubmit(params);
}; };
onBeforeMount(async () => { onBeforeMount(async () => {
if (!id) {
show_message('禁止访问');
return;
}
// 获取组件列表 // 获取组件列表
await getAdminComponent(); await getAdminComponent();
}); });
onBeforeUnmount(() => {
closeInterval();
});
</script> </script>
<style lang="less"> <style lang="less">
@import '@/style/variables.less'; @import '@/style/variables.less';
.custom-copywriting-generation { .custom-copywriting-generation {
margin-top: @page-margin-top; padding-top: @page-margin-top;
box-sizing: border-box;
position: relative;
width: 100%;
height: 100%;
.generation-box {
display: flex; display: flex;
padding-bottom: 30px; padding-bottom: 30px;
box-sizing: border-box;
.generate-result { .generate-result {
width: 50%; width: 50%;
border: 2px dashed #565656; border: 2px dashed #565656;
...@@ -348,7 +396,8 @@ onBeforeMount(async () => { ...@@ -348,7 +396,8 @@ onBeforeMount(async () => {
font-size: @font-size-20; font-size: @font-size-20;
} }
} }
& > :last-child { & > :last-child,
& > :nth-child(2) {
&::before { &::before {
width: calc(200% - 2px); width: calc(200% - 2px);
} }
...@@ -358,5 +407,6 @@ onBeforeMount(async () => { ...@@ -358,5 +407,6 @@ onBeforeMount(async () => {
margin-top: 20px; margin-top: 20px;
} }
} }
}
} }
</style> </style>
...@@ -16,4 +16,9 @@ ...@@ -16,4 +16,9 @@
&::-webkit-scrollbar-thumb { &::-webkit-scrollbar-thumb {
background: #ddd; background: #ddd;
} }
&:hover {
&::-webkit-scrollbar-thumb {
background: #ddd !important;
}
}
} }
// 每个页面的content模块的margin-top // 每个页面的content模块的margin-top
@page-margin-top: 30px; @page-margin-top: 30px;
// border // border
@main-border-radius: 15px; @main-border-radius: 15px;
......
...@@ -2,3 +2,239 @@ export const Tasks = { ...@@ -2,3 +2,239 @@ export const Tasks = {
img_to_img: 1, img_to_img: 1,
text_to_img: 2, text_to_img: 2,
}; };
// 任务类型
export const TASKTYPE = {
CHAT: 1,
PAINTING: 2,
VIDEO: 3,
};
// 文案生成对话记录 本地的key
export const ConversationKey = 'custom_conversation';
// 图片生成页面要push一个生成数量
export const CustomNum = {
name: '生成数量',
lists: [
{
type: 'custom-number',
label: '生成数量',
name: 'number',
value: null,
span: 24,
placeholder: '自定义',
rules: [
{
type: 'required',
message: '产品数量必填',
},
],
},
],
};
// form表单示例--文案
export const FormExample = [
{
name: '基础填写',
lists: [
{
type: 'text',
name: 'category_1',
label: '产品类目',
value: null,
span: 24,
placeholder: '产品类目',
rules: [
{
type: 'required',
message: '必填',
},
],
},
{
type: 'text',
name: 'category_2',
value: null,
label: '产品关键词',
span: 24,
placeholder: '产品关键词',
rules: [
{
type: 'required',
message: '必填',
},
],
},
// 下拉选择
{
type: 'select',
placeholder: '选择产品名称',
label: '产品名称',
name: 'category_3',
options: [
{
label: '充电器',
value: '充电器',
},
{
label: '衣服',
value: '衣服',
},
],
// 可设置默认值
value: '衣服',
rules: [
{
type: 'required',
message: '必填',
},
],
},
],
},
{
name: '详细描述',
lists: [
{
// 长文本输入框
type: 'textarea',
name: 'category_4',
label: '产品描述',
value: null,
span: 24,
maxRows: '5',
minRows: '5',
placeholder: '详细描述产品细节,生成的文案更加完美。',
maxlength: 1000,
rules: [
{
type: 'required',
message: '必填',
},
],
},
],
},
];
// form表单示例2-图片生成
export const FormExample2 = [
{
name: '基础填写',
lists: [
{
type: 'text',
name: 'category_1',
label: '产品类目',
value: null,
span: 24,
placeholder: '产品类目',
rules: [
{
type: 'required',
message: '产品类目必填',
},
],
},
{
type: 'text',
name: 'category_2',
label: '产品类型',
value: null,
span: 24,
placeholder: '产品类型',
rules: [
{
type: 'required',
message: '产品类型必填',
},
],
},
],
},
{
name: '画面描述',
lists: [
{
// 长文本输入框
type: 'textarea',
name: 'category_3',
label: '画面描述',
value: null,
span: 24,
maxRows: '5',
minRows: '5',
placeholder:
'画面描述以短句、短语为佳,高级设置内的参数会对图像生成有艺术修饰作用;支持中英文等语言输入',
maxlength: 1000,
rules: [
{
type: 'required',
message: '画面描述必填',
},
],
},
],
},
// 图片尺寸
{
name: '图片尺寸',
lists: [
{
// 单选按钮组
type: 'radio_group_size',
name: 'size_1',
value: '1600x1600',
span: 24,
label: '图片尺寸',
placeholder: '详细描述产品细节,生成的文案更加完美。',
options: [
{
size: '1600x1600',
label: '亚马逊产品图尺寸',
value: '1600x1600',
},
{
size: '600x180',
label: '亚马逊a+主图',
value: '600x180',
},
{
type: 'custom',
label: '自定义',
value: '',
value1: '',
value2: '',
},
],
rules: [
{
type: 'required',
message: '图片尺寸必选',
},
],
},
],
},
// 上传图片
{
name: '上传图片',
lists: [
{
// 单选按钮组
type: 'upload',
name: 'image_1',
label: '上传图片',
value: null,
span: 24,
// rules: [
// {
// type: 'required',
// message: '必填',
// },
// ],
},
],
},
];
...@@ -3,8 +3,19 @@ import request from '../request'; ...@@ -3,8 +3,19 @@ import request from '../request';
// 场景模块 // 场景模块
// 获取场景列表 // 获取场景列表
export const getScenesList = () => { export const getScenesList = (id: number, type: string) => {
return request.get('/api/scenarios', { if (type == 'parent') {
return request.get(`/api/scenarios`, {
params: {
parent_id: id,
},
headers: {
authorization: `Bearer ${getUserCookie()}`,
},
});
}
let url = id ? `/api/scenarios/${id}` : `/api/scenarios`;
return request.get(url, {
headers: { headers: {
authorization: `Bearer ${getUserCookie()}`, authorization: `Bearer ${getUserCookie()}`,
}, },
...@@ -19,3 +30,50 @@ export const getScenariosRecommend = () => { ...@@ -19,3 +30,50 @@ export const getScenariosRecommend = () => {
}, },
}); });
}; };
// 文案提交
export const useSubmitConversation = (data: any) => {
return request.post('/api/users/conversation', data, {
headers: {
authorization: `Bearer ${getUserCookie()}`,
},
});
};
// 图片任务提交
export const useSubmitConversationImage = (data: any) => {
return request.post('/api/users/conversation/image', data, {
headers: {
authorization: `Bearer ${getUserCookie()}`,
},
});
};
// 图片任务回调
export const useImageCallback = (data: any) => {
return request.get('/api/users/conversation/image', {
params: data,
headers: {
authorization: `Bearer ${getUserCookie()}`,
},
});
};
// 获取上传策略
export const useUploadStrategy = () => {
return request.get('/api/users/upload/policy', {
headers: {
authorization: `Bearer ${getUserCookie()}`,
},
});
};
// 获取生成记录
export const getGenerateRecords = (data: any) => {
return request.get('/api/users/scenarios/logs', {
params: data,
headers: {
authorization: `Bearer ${getUserCookie()}`,
},
});
};
...@@ -411,3 +411,33 @@ export const filterRepeatTimestamp = (list: any, newList: any) => { ...@@ -411,3 +411,33 @@ export const filterRepeatTimestamp = (list: any, newList: any) => {
} }
return list; return list;
}; };
/**
*
* @param rules
* @param value
* @returns
*/
export const Validationrules = (
rules: any[],
value: string | number,
name: string = ''
) => {
if (rules && rules.length) {
for (let i = 0; i < rules.length; i++) {
let item = rules[i];
if (item.type == 'required' && !value && name.indexOf('size_') == -1) {
// 必填项
return item.message;
} else if (item.type == 'regex' && value) {
// 正则
let status = item.value.test(value);
if (!status) {
return item.message;
}
}
}
} else {
return '';
}
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment