detail.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872
  1. <template>
  2. <headerBar title="主图与详情生成" />
  3. <div class="detail-container">
  4. <div>
  5. <!-- 处理模式 -->
  6. <div class="logo-section flex left top" >
  7. <div class="section-title" style="margin-bottom: 0px;">
  8. <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
  9. 处理模式:
  10. <el-checkbox v-model="form.is_only_cutout"
  11. true-value="1"
  12. false-value="0"
  13. true-label="1"
  14. false-label="0"
  15. label="仅抠图模式"/>
  16. <span class="flex left" style="font-weight: normal; font-size: 12px; color: #666">
  17. <el-icon><QuestionFilled /></el-icon>
  18. 如您勾选仅抠图模式,将只做抠图处理,不再自动生成详情页。
  19. </span>
  20. </div>
  21. </div>
  22. <template v-if="form.is_only_cutout == 0">
  23. <!-- 主图LOGO部分 -->
  24. <div class="logo-section flex left top" >
  25. <div class="section-title" style="margin-bottom: 0px;">
  26. <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
  27. 主图LOGO:
  28. </div>
  29. </div>
  30. <div class="logo-section flex left top multi-line">
  31. <upload v-for="item,index in logoList" :value="item" :key="item"
  32. v-show="item"
  33. @input="onRemove(index)"
  34. class="mar-right-10 upload-item"
  35. :class="{
  36. active: item === form.logo_path
  37. }"
  38. @click.native="form.logo_path = item"
  39. ></upload>
  40. <upload @input="onInput"></upload>
  41. </div>
  42. <el-divider />
  43. <!-- &lt;!&ndash; 图片抠图与货号图生成 &ndash;&gt;
  44. <div class="section">
  45. <div class="section-title">
  46. <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
  47. 图片抠图与货号图生成
  48. </div>
  49. <div class="section-content">
  50. <div v-if="showTips" class="instruction-out flex top left">
  51. <img style="fill: #000" src="@/assets/images/xinxi.svg" />
  52. <ol class="instruction-list">
  53. <li>请在下方确认图片拍摄过程中的顺序,确保所有拍摄的图片的顺序一致。</li>
  54. <li>使用中英文语号分隔。</li>
  55. <li>图片的名称不能随意修改,否则无法正常生成详情页。</li>
  56. <li>现有图片名称有:俯视、侧视、后视、鞋底、内里</li>
  57. </ol>
  58. <el-icon @click="showTips = false" class="close-icon">
  59. <Close />
  60. </el-icon>
  61. </div>
  62. &lt;!&ndash; 货号文件夹 &ndash;&gt;
  63. &lt;!&ndash; <div class="form-item">
  64. <div class="label">货号文件夹:</div>
  65. <div class="folder-warp">
  66. <div class="folder-input">
  67. <el-input style="width: 60%;" v-model="folderPath" type="textarea" :rows="2" readonly
  68. placeholder="请选择货号文件夹" />
  69. <el-button class="check-button" type="primary" @click="selectFolder">
  70. <img src="@/assets/images/Photography/wenjian.png" style="width: 14px; " />
  71. 选择目标文件夹</el-button>
  72. </div>
  73. <div class="hint">
  74. <el-icon>
  75. <Warning />
  76. </el-icon> <text>选择货号的上级文件夹</text>
  77. </div>
  78. </div>
  79. </div>
  80. &ndash;&gt;
  81. </div>
  82. </div>
  83. <el-divider />-->
  84. <!-- 选择详情模板部分 -->
  85. <div class="template-section ">
  86. <div class="flex between">
  87. <div class="section-title">
  88. <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
  89. 选择详情模版
  90. </div>
  91. <div class="template-pagination">
  92. <el-pagination background layout="prev, pager, next" v-model:current-page="queryParams.current"
  93. v-model:page-size.sync="queryParams.size" :total="totalPage" @current-change="onCurrentChange"
  94. @size-change="onSizeChange" />
  95. </div>
  96. </div>
  97. <div class="template-list">
  98. <div v-for="(template, index) in visibleTemplates" :key="index" class="template-item"
  99. @click="form.selectTemplate = template">
  100. <el-image :src="template.template_cover_image" fit="contain" class="cur-p"
  101. style="width: 100%; display: block;" />
  102. <div class="select-warp" :class="form.selectTemplate.id == template.id ? 'active' : ''">
  103. <el-icon color="#FFFFFF">
  104. <Select />
  105. </el-icon>
  106. </div>
  107. <div class="template-info">
  108. <span class="mar-left-10 chaochu_1">{{ template.template_name }}</span>
  109. <div class="template-view" @click="viewTemplate(template)">查看</div>
  110. </div>
  111. </div>
  112. </div>
  113. <div class="template-tips c-333 fs-14 line-20 te-l mar-top-20 flex left">
  114. <el-icon><WarningFilled /></el-icon>
  115. <span class="mar-left-10">该模版图片顺序说明:{{form.selectTemplate.template_image_order}}</span>
  116. </div>
  117. </div>
  118. <el-divider />
  119. <!-- 详情高级配置 -->
  120. <!-- <div class="section">
  121. <div class="section-title">
  122. <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
  123. 详情高级配置
  124. </div>
  125. <div class="section-content">
  126. &lt;!&ndash; 图片顺序 &ndash;&gt;
  127. <div class="form-item">
  128. <div class="label">图片顺序:</div>
  129. <el-input v-model="imageOrder" placeholder="请输入图片顺序" class="specific-page-input">
  130. <template #append>
  131. <el-button class="explain-btn" link type="primary">说明</el-button>
  132. </template>
  133. </el-input>
  134. </div>
  135. &lt;!&ndash; 同款检验 &ndash;&gt;
  136. &lt;!&ndash; <div class="form-item">
  137. <div class="label">同款检验:</div>
  138. <el-checkbox v-model="checkSimilar">同款下货号必须齐全</el-checkbox>
  139. </div>
  140. &ndash;&gt;
  141. &lt;!&ndash; 可指定页面独修改 &ndash;&gt;
  142. &lt;!&ndash; <div class="form-item">
  143. <div class="label">可指定页面独修改:</div>
  144. <el-input v-model="specificPage" placeholder="请输入入需要单独修改的页面,示例:4:1 (需修改模版的编号:第一张)"
  145. class="specific-page-input">
  146. <template #append>
  147. <el-button class="explain-btn" link type="primary">说明</el-button>
  148. </template>
  149. </el-input>
  150. </div>
  151. &ndash;&gt;
  152. </div>
  153. </div>
  154. <el-divider />-->
  155. <!-- 详情资料准备部分 -->
  156. <div class="data-prep-section">
  157. <div class="flex-item left">
  158. <div class="section-title">
  159. <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
  160. 详情资料准备 (2选1)
  161. <el-button v-if="form.dataType == '1'" type="text" class="mar-left-10 fs-16" @click="downloadExcel">下载商品基础资料模版</el-button>
  162. </div>
  163. </div>
  164. <div class="flex-item left">
  165. <el-radio-group v-model="form.dataType" class="ml-4">
  166. <el-radio label="1" size="large">EXCEL文件选择</el-radio>
  167. <el-radio label="2" size="large">系统对接(和业务员联系)</el-radio>
  168. </el-radio-group>
  169. </div>
  170. <div v-if="form.dataType == '1'" class="excel-upload">
  171. <div class="flex bottom between">
  172. <div style="max-width: 160px;" class="mar-left-20">商品基础资料EXCEL文件选择:</div>
  173. <div class="flex bottom mar-left-20" style="flex-grow: 1;">
  174. <el-input type="textarea" v-model="form.excel_path" />
  175. </div>
  176. <el-button class="select-button button--primary1 mar-left-20" type="primary" @click="selectExcel">
  177. <img src="@/assets/images/Photography/wenjian.png" style="width: 16px; margin-right: 4px;" />
  178. 选择</el-button>
  179. </div>
  180. </div>
  181. </div>
  182. </template>
  183. </div>
  184. <!-- 底部按钮 -->
  185. <div class="footer">
  186. <!-- <el-button class="button--primary1 footer-button" type="primary" @click="saveConfig">保存配置</el-button> -->
  187. <!-- <el-button class="button--primary1 footer-button" type="primary" @click="startProcess">开始处理</el-button> -->
  188. <el-button class="button--primary1 footer-button" type="primary" @click="generate">开始生成</el-button>
  189. </div>
  190. </div>
  191. <loading-dialog v-if="loadingDialogVisible" v-model="loadingDialogVisible" :requesting="requesting" :progress="progress" :message="message"
  192. :disabled-button="disabledButton" @button-click="handleComplete">
  193. <template v-if="partErrList && partErrList.length > 0" #errList>
  194. <div v-for="(item, idx) in partErrList" :key="idx">
  195. <span>{{ item.goods_art_no }}</span>:<span>{{ item.info }}</span>
  196. </div>
  197. </template>
  198. </loading-dialog>
  199. <el-dialog v-model="dialogVisible">
  200. <img style="width: 100%;" :src="dialogImageUrl" alt="Preview Image" />
  201. </el-dialog>
  202. </template>
  203. <script lang="ts" setup>
  204. import { getCompanyTemplatesApi } from '@/apis/other'
  205. import tokenInfo from '@/stores/modules/token';
  206. import useUserInfo from "@/stores/modules/user";
  207. import { useRoute, useRouter } from 'vue-router'
  208. import { ElMessage, ElMessageBox } from 'element-plus'
  209. import headerBar from '@/components/header-bar/index.vue'
  210. import { ref, computed, reactive, onMounted } from 'vue';
  211. import { Select } from '@element-plus/icons-vue'
  212. import upload from '@/components/upload'
  213. import client from "@/stores/modules/client";
  214. import icpList from '@/utils/ipc'
  215. const clientStore = client();
  216. import { getRouterUrl } from '@/utils/appfun'
  217. import { Close, Warning } from '@element-plus/icons-vue'
  218. import LoadingDialog from '@/views/Photography/components/LoadingDialog.vue'
  219. import { useCheckInfo } from '@/composables/userCheck';
  220. useCheckInfo();
  221. const showTips = ref(true)
  222. const folderPath = ref('') //货号文件夹
  223. // const reportMode = ref('normal') // 抠图模式
  224. const imageOrder = ref('俯视、侧视、后跟、鞋底、内里、组合、组合2、组合3') // 图片顺序
  225. const checkSimilar = ref(false) // 同款检验
  226. const specificPage = ref('') // 可指定页面独修改
  227. // 路由和状态管理初始化
  228. const route = useRoute();
  229. const router = useRouter();
  230. // 完成目录
  231. const completeDirectory = ref('')
  232. const loadingDialogVisible = ref(false)
  233. const progress = ref(0)
  234. const message = ref('正在为您处理,请稍后')
  235. const disabledButton = ref(true)
  236. let templates = ref([])
  237. let goods_art_nos = ref([])
  238. let partErrList = ref([])
  239. const excel_template_url = ref('')
  240. // 是否正在请求接口
  241. const requesting = ref(false)
  242. // 定义一个定时器变量
  243. const INTERVAL = ref<number | NodeJS.Timeout | null>(null);
  244. // 状态变量
  245. const totalPage = ref(0);
  246. const itemsPerPage = 4; // 每页显示的模板数量
  247. const dialogVisible = ref(false);
  248. const dialogImageUrl = ref('');
  249. const queryParams = reactive({ // 分页查询参数
  250. size: 1,
  251. current: 1,
  252. })
  253. const form = reactive({
  254. selectTemplate: {}, //选中的模板
  255. dataType: '1', // 1: 选择excel文件 2: 系统对接
  256. logo_path: '', // 主图LOGO
  257. excel_path: '', // 商品基础资料EXCEL文件选择
  258. is_only_cutout:0, //是否仅抠图模式
  259. })
  260. onMounted(() => {
  261. const goods_art_data = route.query.goods_art_nos
  262. goods_art_nos.value = Array.isArray(goods_art_data) ? goods_art_data : [goods_art_data]
  263. getCompanyTemplates()
  264. getLogolist()
  265. })
  266. // 计算属性,获取当前页可见的模板
  267. const visibleTemplates = computed(() => {
  268. const startIndex = (queryParams.current - 1) * itemsPerPage;
  269. const data = templates.value.slice(startIndex, startIndex + itemsPerPage);
  270. return data
  271. });
  272. // 查看模板详情
  273. const viewTemplate = (template) => {
  274. // 展示大图
  275. dialogVisible.value = true
  276. dialogImageUrl.value = template.template_preview_image
  277. };
  278. // 获取模版列表
  279. const getCompanyTemplates = async () => {
  280. const { data } = await getCompanyTemplatesApi()
  281. templates.value = data.list
  282. // 默认选中第一个模板
  283. if (templates.value.length > 0) {
  284. form.selectTemplate = templates.value[0]
  285. }
  286. excel_template_url.value = data.excel_template_url
  287. // 计算总页数
  288. totalPage.value = Math.ceil(templates.value.length / itemsPerPage);
  289. }
  290. const downloadExcel = () => {
  291. const a = document.createElement('a')
  292. a.href = excel_template_url.value,
  293. a.download = '商品基础资料模版'
  294. document.body.appendChild(a)
  295. a.click()
  296. setTimeout(() => {
  297. document.body.removeChild(a);
  298. }, 1000);
  299. }
  300. const onCurrentChange = (page) => {
  301. queryParams.current = page;
  302. };
  303. const onSizeChange = (data) => {
  304. };
  305. // 开始生成操作
  306. const generate = async function () {
  307. if(form.is_only_cutout == 0 ){
  308. if ( form.dataType == '1' && !form.excel_path) {
  309. ElMessage.error('请上传商品基础资料')
  310. return
  311. }
  312. }
  313. const tokenInfoStore = tokenInfo();
  314. const token = tokenInfoStore.getToken; // 使用 getToken() 获取 token
  315. let temp_list = []
  316. templates.value.map(item => {
  317. temp_list.push({
  318. template_id: item.template_id,
  319. template_local_classes: item.template_local_classes,
  320. })
  321. })
  322. const params = {
  323. goods_art_no: JSON.parse(JSON.stringify(goods_art_nos.value)),
  324. is_only_cutout: form.is_only_cutout || '0',
  325. logo_path: form.logo_path || '',
  326. temp_name: form.selectTemplate.template_id || '',
  327. excel_path: form.dataType == '1' ? form.excel_path : '',
  328. template_image_order: form.selectTemplate.template_image_order,
  329. temp_list,
  330. token,
  331. }
  332. // 开启进度弹窗
  333. requesting.value = false
  334. console.log("params", "color:#3f7cff", params);
  335. partErrList.value = []
  336. message.value = '正在为您处理,请稍后'
  337. progress.value = 0
  338. openLoadingDialog(goods_art_nos.value.length * 10)
  339. clientStore.ipc.removeAllListeners(icpList.generate.generatePhotoDetail);
  340. clientStore.ipc.send(icpList.generate.generatePhotoDetail, params);
  341. clientStore.ipc.on(icpList.generate.generatePhotoDetail, (event, result) => {
  342. console.log('result', result)
  343. requesting.value = true
  344. clientStore.ipc.removeAllListeners(icpList.generate.generatePhotoDetail);
  345. clearInterval(INTERVAL.value)
  346. if (result.code === 0) {
  347. const { output_folder, list } = result.data
  348. const allSuccess = list.every(item => item.success);
  349. const allFailure = list.every(item => !item.success);
  350. if (allSuccess) {
  351. console.log("全部成功")
  352. handleSuccess(output_folder, '全部生成成功')
  353. } else if (allFailure) {
  354. console.log("全部失败");
  355. handleFailure(list)
  356. } else {
  357. console.log("部分成功,部分失败");
  358. handlePartSuccess(output_folder, list)
  359. }
  360. } else {
  361. console.log('code全部生成失败')
  362. handleFail(result.msg)
  363. }
  364. //生成失败 (接口请求失败)
  365. function handleFail(errorMsg: string) {
  366. loadingDialogVisible.value = false
  367. disabledButton.value = false
  368. if (errorMsg) {
  369. ElMessage.error(errorMsg)
  370. }
  371. }
  372. // 全部生成成功
  373. function handleSuccess(href, loadingMsg) {
  374. completeDirectory.value = href
  375. progress.value = 100
  376. disabledButton.value = false
  377. message.value = loadingMsg
  378. }
  379. // 部分成功
  380. function handlePartSuccess(output_folder: string, partSuccessList) {
  381. let errorList = []
  382. partSuccessList.map(item => {
  383. if (!item.success) {
  384. errorList.push(item)
  385. }
  386. })
  387. partErrList.value = errorList
  388. handleSuccess(output_folder, '部分货号生成失败')
  389. }
  390. // 全部生成失败
  391. function handleFailure(partSuccessList) {
  392. let errorList = []
  393. partSuccessList.map(item => {
  394. if (!item.success) {
  395. errorList.push(item)
  396. }
  397. })
  398. partErrList.value = errorList
  399. completeDirectory.value = ''
  400. progress.value = 100
  401. disabledButton.value = true
  402. message.value = '全部货号生成失败'
  403. }
  404. });
  405. }
  406. const openLoadingDialog = (timer: number) => {
  407. loadingDialogVisible.value = true
  408. disabledButton.value = true
  409. // 根据传入的秒数计算每次增加的进度值
  410. const step = 100 / timer
  411. INTERVAL.value = setInterval(() => {
  412. if (progress.value < 100) {
  413. progress.value = Math.min(Math.round(progress.value + step),100)
  414. }
  415. }, 1000)
  416. }
  417. //logo
  418. const logoList = ref([])
  419. const onInput = (value) => {
  420. addLogo(value)
  421. }
  422. const onRemove = (index) => {
  423. if(logoList.value[index] === form.logo_path){
  424. form.logo_path = ''
  425. }
  426. clientStore.ipc.send(icpList.generate.deleteLogo,{
  427. path:logoList.value[index]
  428. });
  429. logoList.value.splice(index, 1)
  430. clientStore.ipc.on(icpList.generate.deleteLogo, async (event, result) => {
  431. console.log('deleteLogo');
  432. console.log(result);
  433. clientStore.ipc.removeAllListeners(icpList.generate.deleteLogo);
  434. })
  435. }
  436. const getLogolist = async () => {
  437. clientStore.ipc.send(icpList.generate.getLogoList);
  438. clientStore.ipc.on(icpList.generate.getLogoList, async (event, result) => {
  439. logoList.value = result.data || []
  440. console.log('getLogoList')
  441. console.log(result.data)
  442. if(logoList.value.length){
  443. form.logo_path = logoList.value[0]
  444. }
  445. clientStore.ipc.removeAllListeners(icpList.generate.getLogoList);
  446. })
  447. }
  448. const addLogo = async (path) => {
  449. console.log(path);
  450. clientStore.ipc.send(icpList.generate.addLogo,{
  451. logo_path:path
  452. });
  453. clientStore.ipc.on(icpList.generate.addLogo, async (event, result) => {
  454. console.log(result);
  455. if (result.code === 0) {
  456. console.log("添加成功")
  457. console.log(result)
  458. if(result.data.logo){
  459. form.logo_path = result.data.logo
  460. if(logoList.value.indexOf(result.data.logo) <0){
  461. logoList.value.push(result.data.logo)
  462. }
  463. }
  464. }
  465. clientStore.ipc.removeAllListeners(icpList.generate.addLogo);
  466. })
  467. }
  468. function selectExcel() {
  469. clientStore.ipc.removeAllListeners(icpList.utils.openFile);
  470. clientStore.ipc.send(icpList.utils.openFile, {
  471. filters: [
  472. { name: '支持xls,xlsx', extensions: ['xlsx', 'xls'] }
  473. ],
  474. title: "选择基础文件资料"
  475. });
  476. clientStore.ipc.on(icpList.utils.openFile, async (event, result) => {
  477. form.excel_path = result
  478. clientStore.ipc.removeAllListeners(icpList.utils.openFile);
  479. })
  480. }
  481. const Router = useRouter()
  482. //打开主图详情
  483. function openPhotographySeniorDetail() {
  484. const { href } = Router.resolve({
  485. name: 'PhotographySeniorDetail'
  486. })
  487. clientStore.ipc.removeAllListeners(icpList.utils.openMain);
  488. let params = {
  489. title: '详情高级配置',
  490. width: 1000,
  491. height: 630,
  492. frame: true,
  493. id: "PhotographySeniorDetail",
  494. url: getRouterUrl(href)
  495. }
  496. clientStore.ipc.send(icpList.utils.openMain, params);
  497. }
  498. const handleComplete = () => {
  499. loadingDialogVisible.value = false
  500. // 这里可以添加打开目录的逻辑
  501. clientStore.ipc.removeAllListeners(icpList.utils.shellFun);
  502. let params = {
  503. action: 'openPath',
  504. params: completeDirectory.value?.replaceAll('/', '\\')
  505. }
  506. clientStore.ipc.send(icpList.utils.shellFun, params);
  507. }
  508. const selectFolder = () => {
  509. clientStore.ipc.removeAllListeners(icpList.utils.openDirectory);
  510. clientStore.ipc.send(icpList.utils.openDirectory);
  511. clientStore.ipc.on(icpList.utils.openDirectory, async (event, result) => {
  512. folderPath.value = result
  513. clientStore.ipc.removeAllListeners(icpList.utils.openDirectory);
  514. })
  515. }
  516. </script>
  517. <style lang="scss" scoped>
  518. .detail-container {
  519. background-color: #EAECED;
  520. width: 100%;
  521. min-width: 600px;
  522. padding: 20px;
  523. overflow: hidden;
  524. min-height: calc(100vh - 30px);
  525. }
  526. .logo-section,
  527. .template-section,
  528. .data-prep-section {
  529. margin-bottom: 20px;
  530. }
  531. .logo-section {
  532. .upload-item {
  533. border: 2px solid rgba(0,0,0,0);
  534. }
  535. .active {
  536. border: 2px solid #2957FF;
  537. border-radius: 6px;
  538. overflow: hidden;;
  539. }
  540. &.multi-line {
  541. flex-direction: row; // 默认横向排列
  542. flex-wrap: wrap; // 允许换行
  543. align-items: flex-start; // 对齐方式调整为顶部对齐
  544. .upload-item {
  545. margin-bottom: 10px; // 每行之间增加间距
  546. width: 90px; // 每行显示 4 个元素,减去外边距
  547. box-sizing: border-box; // 确保宽度计算包含 padding 和 border
  548. }
  549. }
  550. }
  551. .logo-upload {
  552. border: 1px dashed #ccc;
  553. border-radius: 5px;
  554. padding: 50px 0;
  555. text-align: center;
  556. cursor: pointer;
  557. }
  558. .template-pagination button {
  559. margin-right: 5px;
  560. }
  561. .template-pagination span {
  562. display: inline-block;
  563. width: 20px;
  564. height: 20px;
  565. line-height: 20px;
  566. text-align: center;
  567. border: 1px solid #ccc;
  568. border-radius: 5px;
  569. margin-right: 5px;
  570. cursor: pointer;
  571. }
  572. .template-list {
  573. display: flex;
  574. flex-wrap: wrap;
  575. gap: 20px;
  576. margin-top: 10px;
  577. .template-item {
  578. width: calc(25% - 18px);
  579. border: 1px solid #ccc;
  580. border-radius: 10px;
  581. cursor: pointer;
  582. background: #f0f0f0;
  583. position: relative;
  584. overflow: hidden;
  585. .template-info {
  586. position: absolute;
  587. bottom: 0;
  588. left: 0;
  589. background: rgba($color: #000000, $alpha: .3);
  590. width: 100%;
  591. height: 36px;
  592. line-height: 36px;
  593. color: #eee;
  594. display: flex;
  595. align-items: center;
  596. justify-content: space-between;
  597. .template-view {
  598. background: #DFE2E3;
  599. color: #3366FF;
  600. height: 30px;
  601. line-height: 30px;
  602. padding: 0 10px;
  603. border-radius: 4px;
  604. margin-right: 10px;
  605. font-size: 14px;
  606. }
  607. }
  608. }
  609. }
  610. .excel-upload {
  611. width: 100%;
  612. background: #F7F7F7;
  613. padding: 20px 0;
  614. }
  615. .generate-button {
  616. padding: 10px 20px;
  617. color: white;
  618. border: none;
  619. border-radius: 5px;
  620. cursor: pointer;
  621. }
  622. .select-button {
  623. background: #DFE2E3 !important;
  624. color: #3366FF !important;
  625. height: 30px;
  626. line-height: 30px;
  627. padding: 0 10px;
  628. border-radius: 4px;
  629. margin-right: 10px;
  630. font-size: 14px;
  631. font-weight: 550;
  632. }
  633. .select-warp {
  634. width: 18px;
  635. height: 18px;
  636. border-radius: 4px;
  637. background-color: #fff;
  638. position: absolute;
  639. top: 10px;
  640. left: 10px;
  641. &.active {
  642. background-color: #1677FF;
  643. }
  644. }
  645. .section-title {
  646. display: flex;
  647. align-items: center;
  648. gap: 8px;
  649. font-weight: bold;
  650. margin-bottom: 16px;
  651. color: #474747;
  652. }
  653. .section {
  654. margin-bottom: 24px;
  655. .section-title {
  656. display: flex;
  657. align-items: center;
  658. gap: 8px;
  659. font-weight: bold;
  660. margin-bottom: 16px;
  661. color: #474747;
  662. }
  663. .section-content {
  664. padding-left: 16px;
  665. }
  666. }
  667. .instruction-out {
  668. background: #EAF3FF;
  669. border-radius: 4px;
  670. border: 1px solid #CBE1FF;
  671. padding: 10px 20px;
  672. width: 80%;
  673. position: relative;
  674. .instruction-list {
  675. margin: 0px 0 0 10px;
  676. padding-left: 20px;
  677. padding-right: 40px;
  678. width: 100%;
  679. li {
  680. margin-bottom: 4px;
  681. text-align: left;
  682. font-size: 14px;
  683. }
  684. }
  685. .close-icon {
  686. position: absolute;
  687. top: 12px;
  688. right: 19px;
  689. }
  690. }
  691. .form-item {
  692. margin: 16px 0;
  693. display: flex;
  694. .label {
  695. min-width: 120px;
  696. margin-right: 12px;
  697. }
  698. .folder-warp {
  699. width: 100%;
  700. display: flex;
  701. flex-direction: column;
  702. .folder-input {
  703. flex: 1;
  704. display: flex;
  705. align-items: center;
  706. .check-button {
  707. background: #DFE2E3;
  708. border-radius: 6px;
  709. padding: 6px 12px;
  710. color: #2957FF;
  711. margin-left: 20px;
  712. }
  713. }
  714. }
  715. .hint {
  716. text-align: left;
  717. color: #999;
  718. margin-top: 6px;
  719. font-size: 14px;
  720. color: #FF4C00;
  721. font-style: normal;
  722. }
  723. .specific-page-input {
  724. flex: 1;
  725. }
  726. }
  727. .image-order {
  728. flex: 1;
  729. display: flex;
  730. justify-content: space-between;
  731. align-items: center;
  732. }
  733. .footer {
  734. display: flex;
  735. justify-content: center;
  736. margin-top: 24px;
  737. .footer-button {
  738. padding: 10px 20px;
  739. }
  740. }
  741. .explain-btn {
  742. padding-left: 20px;
  743. padding-right: 20px;
  744. color: #2957FF !important;
  745. }
  746. </style>