diff --git a/ruoyi-office/src/main/java/com/ruoyi/office/domain/TtCopyTemplate.java b/ruoyi-office/src/main/java/com/ruoyi/office/domain/TtCopyTemplate.java index b8e9ca2..c316e45 100644 --- a/ruoyi-office/src/main/java/com/ruoyi/office/domain/TtCopyTemplate.java +++ b/ruoyi-office/src/main/java/com/ruoyi/office/domain/TtCopyTemplate.java @@ -21,6 +21,10 @@ public class TtCopyTemplate extends BaseEntity { @Excel(name = "按钮名称") private String templateName; + /** 模板分组 */ + @Excel(name = "模板分组") + private String templateGroup; + /** 文案模板内容,支持 {变量} 占位符 */ @Excel(name = "模板内容") private String templateBody; @@ -49,6 +53,14 @@ public class TtCopyTemplate extends BaseEntity { return templateName; } + public void setTemplateGroup(String templateGroup) { + this.templateGroup = templateGroup; + } + + public String getTemplateGroup() { + return templateGroup; + } + public void setTemplateBody(String templateBody) { this.templateBody = templateBody; } @@ -78,6 +90,7 @@ public class TtCopyTemplate extends BaseEntity { return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) .append("templateId", getTemplateId()) .append("templateName", getTemplateName()) + .append("templateGroup", getTemplateGroup()) .append("templateBody", getTemplateBody()) .append("sortNum", getSortNum()) .append("status", getStatus()) @@ -85,4 +98,4 @@ public class TtCopyTemplate extends BaseEntity { .append("updateTime", getUpdateTime()) .toString(); } -} \ No newline at end of file +} diff --git a/ruoyi-office/src/main/resources/mapper/office/TtCopyTemplateMapper.xml b/ruoyi-office/src/main/resources/mapper/office/TtCopyTemplateMapper.xml index bf933eb..f2cbf1d 100644 --- a/ruoyi-office/src/main/resources/mapper/office/TtCopyTemplateMapper.xml +++ b/ruoyi-office/src/main/resources/mapper/office/TtCopyTemplateMapper.xml @@ -7,6 +7,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -15,7 +16,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select template_id, template_name, template_body, sort_num, status, create_time, update_time + select template_id, template_name, template_group, template_body, sort_num, status, create_time, update_time from tt_copy_template @@ -23,6 +24,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and template_name like concat('%', #{templateName}, '%') + and template_group like concat('%', #{templateGroup}, '%') and status = #{status} order by sort_num asc, create_time asc @@ -43,6 +45,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" insert into tt_copy_template template_name, + template_group, template_body, sort_num, status, @@ -51,6 +54,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{templateName}, + #{templateGroup}, #{templateBody}, #{sortNum}, #{status}, @@ -63,6 +67,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" update tt_copy_template template_name = #{templateName}, + template_group = #{templateGroup}, template_body = #{templateBody}, sort_num = #{sortNum}, status = #{status}, @@ -82,4 +87,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - \ No newline at end of file + diff --git a/ruoyi-ui/src/views/office/code/index.vue b/ruoyi-ui/src/views/office/code/index.vue index ddb40ca..c695dfd 100644 --- a/ruoyi-ui/src/views/office/code/index.vue +++ b/ruoyi-ui/src/views/office/code/index.vue @@ -154,6 +154,14 @@ v-hasPermi="['office:code:query']" >查看 + 文案 + + + + + +
+ + + {{ group.name }} + {{ group.templates.length }} + + + +
+
+
模板列表
+
+ +
+
暂无启用的文案模板
+
+ +
+
文案预览
+
{{ copywritingContent }}
+ +
+
+
+ 暂无启用的文案模板 +
+
+
+ @@ -340,6 +432,14 @@ // 是否显示弹出层 open: false, openDetail: false, + // 文案预览弹窗 + copywritingDialogVisible: false, + copywritingLoading: false, + copywritingProject: {}, + copywritingTemplateId: null, + copywritingGroupName: '', + copywritingCopied: false, + copywritingCopyTimer: null, // 查询参数 queryParams: { pageNum: 1, @@ -383,11 +483,36 @@ this.getList(); this.loadCopyTemplates(); }, + beforeDestroy() { + if (this.copywritingCopyTimer) { + clearTimeout(this.copywritingCopyTimer); + } + }, computed: { previewList() { return this.form.fileList.map(i => { return i.fileUrl }) + }, + copywritingContent() { + const template = this.copyTemplates.find(item => item.templateId === this.copywritingTemplateId); + return template ? this.renderCopyTemplate(template, this.copywritingProject) : ''; + }, + groupedCopyTemplates() { + return this.copyTemplates.reduce((groups, template) => { + const groupName = (template.templateGroup || '').trim() || '默认分组'; + let group = groups.find(item => item.name === groupName); + if (!group) { + group = { name: groupName, templates: [] }; + groups.push(group); + } + group.templates.push(template); + return groups; + }, []); + }, + activeCopywritingTemplates() { + const group = this.groupedCopyTemplates.find(item => item.name === this.copywritingGroupName); + return group ? group.templates : []; } }, methods: { @@ -426,9 +551,10 @@ }; this.resetForm("form"); }, - copyByTemplate(tpl) { + renderCopyTemplate(tpl, source) { // 从数据库模板中提取并替换占位符 - let codeName = this.form.codeName || ''; + const code = source || {}; + let codeName = code.codeName || ''; let projectCode = ''; let projectName = codeName; @@ -448,16 +574,16 @@ // 项目描述转纯文本 let plainDesc = ''; - if (this.form.codeDesc) { - let codeDesc = this.form.codeDesc.replace(/<\/p>/g, '

\n'); + if (code.codeDesc) { + let codeDesc = code.codeDesc.replace(/<\/p>/g, '

\n'); plainDesc = html2Text(codeDesc.trimEnd()); } // 生成截图列表 let screenshots = ''; let index = 0; - if (this.form.fileList && this.form.fileList.length > 0) { - for (let item of this.form.fileList) { + if (code.fileList && code.fileList.length > 0) { + for (let item of code.fileList) { screenshots = screenshots + ++index + '.' + item.fileName + '\n![](' + item.fileUrl + ')\n\n'; } } else { @@ -470,13 +596,17 @@ content = content.replace(/{projectCode}/g, projectCode); content = content.replace(/{projectName}/g, projectName); content = content.replace(/{codeDesc}/g, plainDesc); - content = content.replace(/{codeEnvironment}/g, this.form.codeEnvironment || ''); - content = content.replace(/{frontendTechnology}/g, this.form.frontendTechnology || ''); - content = content.replace(/{backendTechnology}/g, this.form.backendTechnology || ''); - content = content.replace(/{databaseTechnology}/g, this.form.databaseTechnology || ''); - content = content.replace(/{codeTechnology}/g, this.form.codeTechnology || ''); - content = content.replace(/{diskLink}/g, this.form.diskLink || ''); + content = content.replace(/{codeEnvironment}/g, code.codeEnvironment || ''); + content = content.replace(/{frontendTechnology}/g, code.frontendTechnology || ''); + content = content.replace(/{backendTechnology}/g, code.backendTechnology || ''); + content = content.replace(/{databaseTechnology}/g, code.databaseTechnology || ''); + content = content.replace(/{codeTechnology}/g, code.codeTechnology || ''); + content = content.replace(/{diskLink}/g, code.diskLink || ''); content = content.replace(/{screenshots}/g, screenshots); + return content; + }, + copyByTemplate(tpl) { + const content = this.renderCopyTemplate(tpl, this.form); navigator.clipboard.writeText(content).then(() => { this.$message({ message: '复制成功', type: 'success' }); @@ -488,8 +618,49 @@ listEnabledTemplates().then(response => { this.copyTemplates = response.data || []; this.selectDefaultArticleTemplate(); + this.selectDefaultCopywritingTemplate(); }); }, + selectDefaultCopywritingTemplate() { + if (!this.copyTemplates.length) { + this.copywritingTemplateId = null; + this.copywritingGroupName = ''; + return; + } + let selectedTemplate = this.copyTemplates.find(tpl => tpl.templateId === this.copywritingTemplateId); + if (!selectedTemplate) { + let lastTemplateId = ''; + try { + lastTemplateId = window.localStorage.getItem('officeCopywritingTemplateId') || ''; + } catch (error) { + lastTemplateId = ''; + } + selectedTemplate = this.copyTemplates.find(tpl => String(tpl.templateId) === lastTemplateId) + || this.copyTemplates[0]; + } + this.selectCopywritingTemplate(selectedTemplate); + }, + getCopywritingGroupName(template) { + return ((template && template.templateGroup) || '').trim() || '默认分组'; + }, + selectCopywritingTemplate(template) { + if (!template) { + return; + } + this.copywritingTemplateId = template.templateId; + this.copywritingGroupName = this.getCopywritingGroupName(template); + this.copywritingCopied = false; + try { + window.localStorage.setItem('officeCopywritingTemplateId', String(template.templateId)); + } catch (error) { + } + }, + handleCopywritingGroupChange(groupName) { + const group = this.groupedCopyTemplates.find(item => item.name === groupName); + if (group && group.templates.length) { + this.selectCopywritingTemplate(group.templates[0]); + } + }, selectDefaultArticleTemplate() { if (!this.copyTemplates.length) { this.articleTemplateId = null; @@ -584,6 +755,57 @@ this.title = "查看源码信息"; }); }, + /** 文案预览按钮操作 */ + handleCopywriting(row) { + this.copywritingLoading = true; + this.copywritingProject = {}; + this.copywritingTemplateId = null; + this.copywritingGroupName = ''; + this.copywritingCopied = false; + this.copywritingDialogVisible = true; + this.selectDefaultCopywritingTemplate(); + getCode(row.codeId).then(response => { + this.copywritingProject = response.data || {}; + }).catch(() => { + this.copywritingDialogVisible = false; + }).finally(() => { + this.copywritingLoading = false; + }); + }, + resetCopywritingDialog() { + if (this.copywritingCopyTimer) { + clearTimeout(this.copywritingCopyTimer); + this.copywritingCopyTimer = null; + } + this.copywritingProject = {}; + this.copywritingTemplateId = null; + this.copywritingGroupName = ''; + this.copywritingCopied = false; + this.copywritingLoading = false; + }, + copyCurrentWriting(closeAfterCopy) { + if (!this.copywritingTemplateId) { + this.$modal.msgError('请选择文案模板'); + return; + } + navigator.clipboard.writeText(this.copywritingContent).then(() => { + if (closeAfterCopy) { + this.copywritingDialogVisible = false; + this.$modal.msgSuccess('文案复制成功'); + return; + } + this.copywritingCopied = true; + if (this.copywritingCopyTimer) { + clearTimeout(this.copywritingCopyTimer); + } + this.copywritingCopyTimer = setTimeout(() => { + this.copywritingCopied = false; + this.copywritingCopyTimer = null; + }, 1800); + }).catch(error => { + this.$modal.msgError('复制失败:' + error); + }); + }, showCoverDialog(row) { this.currentRow = row this.selectDefaultArticleTemplate() @@ -702,3 +924,198 @@ } }; + + diff --git a/ruoyi-ui/src/views/office/copyTemplate/index.vue b/ruoyi-ui/src/views/office/copyTemplate/index.vue index 4a51b7a..e8153c9 100644 --- a/ruoyi-ui/src/views/office/copyTemplate/index.vue +++ b/ruoyi-ui/src/views/office/copyTemplate/index.vue @@ -9,6 +9,14 @@ @keyup.enter.native="handleQuery" /> + + + @@ -37,6 +45,7 @@ +