mirror of
https://github.com/palxiao/poster-design.git
synced 2025-07-28 04:10:31 +08:00
feat: keyCode menu
This commit is contained in:
parent
50d193c067
commit
74b0395b66
8
package-lock.json
generated
8
package-lock.json
generated
@ -19,7 +19,7 @@
|
|||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"cropperjs": "^1.6.1",
|
"cropperjs": "^1.6.1",
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
"element-plus": "^2.3.7",
|
"element-plus": "^2.6.3",
|
||||||
"fontfaceobserver": "^2.1.0",
|
"fontfaceobserver": "^2.1.0",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
@ -1972,9 +1972,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/element-plus": {
|
"node_modules/element-plus": {
|
||||||
"version": "2.6.1",
|
"version": "2.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.6.3.tgz",
|
||||||
"integrity": "sha512-6VRpLjwtIVdtUuITJPPKtpOH1NM6nuAkRE3q5O4Lrx0N1bYMhTkiqb2Jy7zfQuDPbOIkkF2OABTzegpNnzgsnQ==",
|
"integrity": "sha512-U4L/mr+1r+EmAUYUHrs0V/8hHMdBGP07rPymSC72LZCN4jK1UwygQYICegTQ5us4mxeqBvW6wfoEfo003fwCqw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ctrl/tinycolor": "^3.4.1",
|
"@ctrl/tinycolor": "^3.4.1",
|
||||||
"@element-plus/icons-vue": "^2.3.1",
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"cropperjs": "^1.6.1",
|
"cropperjs": "^1.6.1",
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
"element-plus": "^2.3.7",
|
"element-plus": "^2.6.3",
|
||||||
"fontfaceobserver": "^2.1.0",
|
"fontfaceobserver": "^2.1.0",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
|
@ -55,6 +55,16 @@
|
|||||||
&-item {
|
&-item {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
.icon {
|
||||||
|
// font-size: 18px;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: .4rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.disable {
|
.disable {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
@ -62,37 +72,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// .top-title {
|
.primary-btn {
|
||||||
// color: @color-black;
|
font-weight: 600;
|
||||||
// cursor: pointer;
|
transform: scale(0.95);
|
||||||
// flex: 1;
|
margin-left: 10px;
|
||||||
// padding-left: 3.2rem;
|
}
|
||||||
// // font-weight: bold;
|
|
||||||
// .input-wrap {
|
|
||||||
// width: 15rem;
|
|
||||||
// :deep(input) {
|
|
||||||
// border: none;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .top-icon-wrap {
|
|
||||||
// display: flex;
|
|
||||||
// align-items: center;
|
|
||||||
// padding-right: 20px;
|
|
||||||
// height: @height2;
|
|
||||||
// .top-icon {
|
|
||||||
// background-color: rgba(0, 0, 0, 0.4);
|
|
||||||
// border-radius: 5px;
|
|
||||||
// color: @color-white;
|
|
||||||
// cursor: pointer;
|
|
||||||
// font-weight: bold;
|
|
||||||
// margin: 8px;
|
|
||||||
// padding: 5px 8px;
|
|
||||||
// &:hover {
|
|
||||||
// background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.page-design-index-wrap {
|
.page-design-index-wrap {
|
||||||
@ -109,21 +93,6 @@
|
|||||||
// .page-design-index-wrap ::-webkit-scrollbar {
|
// .page-design-index-wrap ::-webkit-scrollbar {
|
||||||
// display: none; /* Chrome Safari */
|
// display: none; /* Chrome Safari */
|
||||||
// }
|
// }
|
||||||
|
|
||||||
.extra-operation {
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
margin-left: 52px;
|
|
||||||
}
|
|
||||||
.extra-operation::after {
|
|
||||||
position: absolute;
|
|
||||||
content: '';
|
|
||||||
left: -50px;
|
|
||||||
background: #e8eaec;
|
|
||||||
height: 17px;
|
|
||||||
width: 1px;
|
|
||||||
margin: 7px 0 0 18px;
|
|
||||||
}
|
|
||||||
.shelter,
|
.shelter,
|
||||||
.shelter-bg {
|
.shelter-bg {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -1,578 +0,0 @@
|
|||||||
<!--
|
|
||||||
* @Author: ShawnPhang
|
|
||||||
* @Date: 2024-01-25 10:35:37
|
|
||||||
* @Description: SD
|
|
||||||
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
|
||||||
* @LastEditTime: 2024-02-27 21:52:57
|
|
||||||
-->
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<el-dialog v-model="show" align-center style="width: 88%; max-width: 980px" @close="handleClose">
|
|
||||||
<template #header> AIGC(动漫风格大模型) </template>
|
|
||||||
<div class="sd-box">
|
|
||||||
<div class="left">
|
|
||||||
<div class="input-wrap">
|
|
||||||
<el-radio-group v-model="typeIndex" type="card" @change="typeChange">
|
|
||||||
<el-radio-button :value="0">文生图</el-radio-button>
|
|
||||||
<el-radio-button :value="1">图生图</el-radio-button>
|
|
||||||
<el-radio-button disabled :value="2">后期处理</el-radio-button>
|
|
||||||
</el-radio-group>
|
|
||||||
<div v-show="typeIndex == 1" class="wrap">
|
|
||||||
<h2 class="wrap-title">参考图</h2>
|
|
||||||
<uploader :hold="true" :drag="true" :multiple="false" class="uploader" @load="selectFile">
|
|
||||||
<div v-show="progress !== 0" class="mask">
|
|
||||||
<el-progress type="circle" :percentage="progress" />
|
|
||||||
</div>
|
|
||||||
<img v-if="localImage" :src="localImage" />
|
|
||||||
<div v-else class="uploader__box">
|
|
||||||
<icon-upload style="width: 64px; height: 64px" />
|
|
||||||
<div class="el-upload__text">在此拖入或选择<em>上传图片</em></div>
|
|
||||||
<div class="el-upload__tip">支持 jpg/png 格式,大小不超过 500kb </div>
|
|
||||||
</div>
|
|
||||||
</uploader>
|
|
||||||
<br />
|
|
||||||
<number-slider v-model="controlNet.weight" style="font-size: 14px" label="权重" :step="0.05" :maxValue="2" />
|
|
||||||
<el-radio-group v-model="controlNet.mode">
|
|
||||||
<el-radio :value="0">均衡</el-radio>
|
|
||||||
<el-radio :value="1">偏向提示词</el-radio>
|
|
||||||
<el-radio :value="2">偏向原图</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</div>
|
|
||||||
<div class="wrap">
|
|
||||||
<h2 class="wrap-title">提示词 (不支持中文)</h2>
|
|
||||||
<el-input v-model="params.prompt" maxlength="300" :autosize="{ minRows: 4, maxRows: 6 }" placeholder="输入引导提示词,仅支持解析英文,若输入中文请翻译文本后再提交" show-word-limit type="textarea" @input="promptTyping" />
|
|
||||||
<div style="display: flex; align-items: center; margin-top: 5px; justify-content: space-between">
|
|
||||||
<el-button :loading="isLoading" type="primary" link @click="translation"><i v-show="!isLoading" style="margin-right: 5px" class="icon sd-fanyi" />翻译文本</el-button>
|
|
||||||
<tool-tip :width="260" placement="bottom-end" effect="dark" :content="tips">
|
|
||||||
<span v-show="tips" class="mini-text" style="margin-top: 2px">相关提示词</span>
|
|
||||||
</tool-tip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-show="typeIndex == 0" class="wrap">
|
|
||||||
<h2 class="wrap-title">套用预设</h2>
|
|
||||||
<div class="presuppose-box">
|
|
||||||
<div v-for="p in presuppose" :key="p.value" :class="['presuppose', { p_select: p.value === params.depend }]" @click="setDepend(p)">
|
|
||||||
<div class="img" :style="{ backgroundImage: `url(${p.url})` }">
|
|
||||||
<div v-if="p.value === params.depend" class="cancel">取消</div>
|
|
||||||
<div class="mask">{{ p.name }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="wrap">
|
|
||||||
<h2 class="wrap-title">图片比例</h2>
|
|
||||||
<el-select v-model="params.ratio" placeholder="选择比例" style="width: 100%">
|
|
||||||
<el-option v-for="item in ratios" :key="item.value" :label="item.label" :value="item.value">
|
|
||||||
<span style="float: left"><i style="margin-right: 1em" :class="['icon', 'sd-scale' + item.value]" />{{ item.label }}</span>
|
|
||||||
<span style="float: right" class="mini-text">{{ item.text }}</span>
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
<div class="wrap">
|
|
||||||
<h2 class="wrap-title">高级设置</h2>
|
|
||||||
<el-switch v-model="params.is_lcm" active-text="开启极速出图" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="buttom-wrap">
|
|
||||||
<div class="mianze">
|
|
||||||
<tool-tip :width="260" placement="top-end" effect="dark" content="本服务基于开源AI大模型开发,生成内容不代表本站观点,请遵守法规使用,并承担所有产生的责任。">
|
|
||||||
<span style="display: flex"><InfoFilled width="14" />免责声明</span>
|
|
||||||
</tool-tip>
|
|
||||||
</div>
|
|
||||||
<el-button type="primary" style="width: 100%" @click="submit">在线生成</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="images-box">
|
|
||||||
<div class="box-header">
|
|
||||||
<div style="flex: 1">
|
|
||||||
<el-button :loading="listLoading" link @click="refreshList"><Refresh v-if="!listLoading" style="margin-right: 7px" width="14" />{{ listLoading ? '刷新中...' : '任务列表' }}</el-button>
|
|
||||||
</div>
|
|
||||||
<div class="limit"><span class="mini-text">空间容量:</span> {{ listData.length }} / {{ limit }}</div>
|
|
||||||
</div>
|
|
||||||
<ul v-if="listData.length > 0" ref="listRef" class="result-list" style="overflow: auto">
|
|
||||||
<li v-for="i in listData" :key="'l' + i.id">
|
|
||||||
<div class="mini-text title">
|
|
||||||
{{ i.created_time }}
|
|
||||||
<tool-tip placement="top" :hide-after="10" :offset="6" content="下载图片到本地">
|
|
||||||
<el-button v-show="i.url" style="margin-left: 8px" type="info" link @click="donwload(i)"><icon-download style="margin-right: 4px" width="14" />{{ i.dp ? '下载进度:' + i.dp + '%' : '' }}</el-button>
|
|
||||||
</tool-tip>
|
|
||||||
<tool-tip placement="top" :hide-after="10" :offset="6" content="发送到「图生图」">
|
|
||||||
<el-button v-show="i.url" style="margin-left: 0" type="info" link @click="exportToImg(i)"><icon-upload style="margin-right: 4px" width="14" /></el-button>
|
|
||||||
</tool-tip>
|
|
||||||
<tool-tip placement="top" :hide-after="10" :offset="6" content="导入设计器背景图">
|
|
||||||
<el-button v-show="i.url" style="margin-left: 0" type="info" link @click="exportToPage(i)"><icon-switch style="margin-right: 4px" width="14" /></el-button>
|
|
||||||
</tool-tip>
|
|
||||||
<el-popconfirm title="是否确认删除?" confirm-button-text="确认" cancel-button-text="取消" @confirm="del(i.id)">
|
|
||||||
<template #reference>
|
|
||||||
<el-button v-show="i.state" style="margin-left: 0" type="info" link><icon-delete width="14" /></el-button>
|
|
||||||
</template>
|
|
||||||
</el-popconfirm>
|
|
||||||
</div>
|
|
||||||
<img v-if="i.nsfw" :style="{ width: '220px' }" src="https://store.palxp.cn/no_safe.jpg" />
|
|
||||||
<el-progress v-else-if="!i.url" :percentage="i.progress" :status="i.state==2?'exception':''" :stroke-width="15" striped striped-flow :duration="10">
|
|
||||||
<div v-if="i.state==2" style="padding: 0 12px 0 4px" class="mini-text">生成失败</div>
|
|
||||||
<div v-else style="padding: 0 12px 0 4px" class="mini-text">{{ i.progress ? `生成中 ${i.progress} %` : '排队中...' }}</div>
|
|
||||||
</el-progress>
|
|
||||||
<el-image v-else :style="{ height: getViewHeight(i) }" class="result-image" :src="i.url.replace('.png', '_tiny.png')" fit="fill" hide-on-click-modal :lazy="true" :preview-src-list="[i.url]" />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<el-empty v-else v-loading="listLoading" style="height: 80%" description="还没有图片,请从左侧面板中点击生成" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { reactive, toRefs, ref } from 'vue'
|
|
||||||
import { ElOption, ElSelect, ElEmpty, ElRadioGroup, ElRadioButton, ElRadio, ElProgress, ElPopconfirm } from 'element-plus'
|
|
||||||
import { InfoFilled, Refresh, Delete as IconDelete, Download as IconDownload, Switch as IconSwitch, Picture as IconUpload } from '@element-plus/icons-vue'
|
|
||||||
import toolTip from '@/components/common/PopoverTip.vue'
|
|
||||||
import { useControlStore, useCanvasStore } from '@/store'
|
|
||||||
import api from '@/api'
|
|
||||||
import useNotification from '@/common/methods/notification'
|
|
||||||
import ratios from './ratioOptions.ts'
|
|
||||||
import presuppose from './presuppose.ts'
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import downloadImg from '@/common/methods/download/download.ts'
|
|
||||||
import uploader from '@/components/common/Uploader/index.vue'
|
|
||||||
import numberSlider from '@/components/modules/settings/numberSlider.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: { ElPopconfirm, ElOption, ElSelect, ElEmpty, toolTip, InfoFilled, ElRadioGroup, ElRadioButton, ElRadio, Refresh, IconDownload, IconDelete, ElProgress, IconSwitch, IconUpload, uploader, numberSlider },
|
|
||||||
setup() {
|
|
||||||
const controlStore = useControlStore()
|
|
||||||
const pageStore = useCanvasStore()
|
|
||||||
const promptRecord = ['','']
|
|
||||||
const state = reactive({
|
|
||||||
show: false,
|
|
||||||
isLoading: false,
|
|
||||||
listLoading: false,
|
|
||||||
typeIndex: 0, // 0 文生图 1 图生图 2 高清修复
|
|
||||||
params: { prompt: '', ratio: '1_1', is_lcm: true, img_url: '' },
|
|
||||||
localImage: '', // 未提交上传的图片
|
|
||||||
controlNet: { weight: 0.5, mode: 0 },
|
|
||||||
progress: 0,
|
|
||||||
limit: '-',
|
|
||||||
listData: [],
|
|
||||||
tips: '', // 预设提示词
|
|
||||||
fileList: []
|
|
||||||
})
|
|
||||||
let localImageFile = null
|
|
||||||
const listRef = ref(null)
|
|
||||||
|
|
||||||
const getListData = (toTop = true) => {
|
|
||||||
state.listLoading = true
|
|
||||||
api.ai
|
|
||||||
.getTaskList()
|
|
||||||
.then((res) => {
|
|
||||||
state.limit = res.limit
|
|
||||||
state.listData = res.list.map((x) => {
|
|
||||||
x.created_time = dayjs(x.created_time).format('YYYY年MM月DD日 HH:mm:ss')
|
|
||||||
return x
|
|
||||||
})
|
|
||||||
setTimeout(() => {
|
|
||||||
listRef.value && toTop && (listRef.value.scrollTop = 0)
|
|
||||||
state.listLoading = false
|
|
||||||
}, 300)
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
state.listLoading = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const open = async () => {
|
|
||||||
controlStore.setShowMoveable(false)
|
|
||||||
state.show = true
|
|
||||||
getListData()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
controlStore.setShowMoveable(true)
|
|
||||||
clearTimeout(curTimer)
|
|
||||||
}
|
|
||||||
|
|
||||||
const submit = async () => {
|
|
||||||
try {
|
|
||||||
if (containsChinese(state.params.prompt)) {
|
|
||||||
await translation()
|
|
||||||
}
|
|
||||||
let control_net = ''
|
|
||||||
if (state.typeIndex == 1) {
|
|
||||||
if (state.progress !== 0) {
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
state.progress = 1
|
|
||||||
await fileUpload()
|
|
||||||
}
|
|
||||||
const {weight, mode} = state.controlNet
|
|
||||||
control_net = JSON.stringify([weight, mode])
|
|
||||||
}
|
|
||||||
const id = await api.ai.addTask({...state.params, type: state.typeIndex, control_net})
|
|
||||||
useNotification('创建成功', '请在任务列表中查看', { type: 'success' })
|
|
||||||
await getListData()
|
|
||||||
taskTimerQueue.push({ id, count: 0 })
|
|
||||||
startTaskRunner()
|
|
||||||
} catch (error) {
|
|
||||||
state.progress = 0
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取详情
|
|
||||||
const taskTimerQueue = [] // task轮训队列
|
|
||||||
let taskItem = {} // 正在修改的任务
|
|
||||||
const reTryNum = 50 // 重试次数
|
|
||||||
const timeInterval = 2000
|
|
||||||
let curTimer = null // 当前定时器
|
|
||||||
const startTaskRunner = () => {
|
|
||||||
if (!curTimer) {
|
|
||||||
const task = taskTimerQueue.shift()
|
|
||||||
if (task) {
|
|
||||||
curTimer = setTimeout(() => {
|
|
||||||
getTask(task)
|
|
||||||
}, timeInterval)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const getTask = async (task) => {
|
|
||||||
const res = await api.ai.getTask({ id: task.id })
|
|
||||||
taskItem = state.listData.find((x) => x.id == task.id)
|
|
||||||
taskItem.progress = res.progress
|
|
||||||
taskItem.state = res.state
|
|
||||||
taskItem.nsfw = res.nsfw
|
|
||||||
taskItem.url = res.url
|
|
||||||
task.count += 1
|
|
||||||
if (task.count > reTryNum || res.state) {
|
|
||||||
curTimer = null
|
|
||||||
startTaskRunner()
|
|
||||||
} else {
|
|
||||||
curTimer = setTimeout(() => {
|
|
||||||
getTask(task)
|
|
||||||
}, timeInterval)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 翻译中英
|
|
||||||
const translation = async () => {
|
|
||||||
state.isLoading = true
|
|
||||||
try {
|
|
||||||
const res = await api.ai.translation({ text: state.params.prompt })
|
|
||||||
state.params.prompt = res
|
|
||||||
} catch (error) {}
|
|
||||||
state.isLoading = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 刷新列表
|
|
||||||
const refreshList = () => {
|
|
||||||
getListData()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除
|
|
||||||
const del = async (id) => {
|
|
||||||
await api.ai.delTask({ id })
|
|
||||||
getListData(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 下载图片
|
|
||||||
const donwload = (i) => {
|
|
||||||
if (!i.dp) {
|
|
||||||
downloadImg(i.url, (d) => {
|
|
||||||
i.dp = d === 100 ? null : d.toFixed(2)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 导入画布
|
|
||||||
const exportToPage = (i) => {
|
|
||||||
const { url, width, height } = i
|
|
||||||
pageStore.dPage.backgroundImage = url
|
|
||||||
pageStore.dPage.width = width
|
|
||||||
pageStore.dPage.height = height
|
|
||||||
}
|
|
||||||
// 导入图生图
|
|
||||||
const exportToImg = ({url}) => {
|
|
||||||
state.localImage = url.replace('.png', '_tiny.png')
|
|
||||||
state.params.img_url = url
|
|
||||||
state.typeIndex = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// 选择图片
|
|
||||||
const selectFile = async (file) => {
|
|
||||||
state.progress = 0
|
|
||||||
if (file.size > 1024 * 1024 / 2) {
|
|
||||||
state.localImage = ''
|
|
||||||
useNotification('上传图片超出限制', '请重新选择图片上传', { type: 'error' })
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// 显示选择的图片
|
|
||||||
localImageFile = file
|
|
||||||
state.localImage = URL.createObjectURL(file)
|
|
||||||
// 清空图片缓存
|
|
||||||
state.params.img_url = ''
|
|
||||||
}
|
|
||||||
// 上传图片
|
|
||||||
const fileUpload = async () => {
|
|
||||||
if (!state.params.img_url && state.localImage) {
|
|
||||||
// 上传本地图片
|
|
||||||
const name = (new Date()).getTime()+'_'+(Math.floor(Math.random() * 990000) + 10000)
|
|
||||||
const result = await api.material.upload({file: localImageFile, name}, (up, dp) => {
|
|
||||||
if (up < 100) {
|
|
||||||
state.progress = up
|
|
||||||
} else {
|
|
||||||
state.progress = 100
|
|
||||||
setTimeout(() => {
|
|
||||||
state.progress = 0
|
|
||||||
}, 600);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
state.params.img_url = result.url
|
|
||||||
} else state.progress = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const setDepend = (p) => {
|
|
||||||
state.params.depend = state.params.depend === p.value ? '' : p.value
|
|
||||||
state.params.prompt = state.params.depend ? p.prompt : ''
|
|
||||||
state.params.ratio = state.params.depend ? p.ratio : '1_1'
|
|
||||||
state.tips = state.params.depend ? p.tips || '' : ''
|
|
||||||
}
|
|
||||||
|
|
||||||
const getViewHeight = ({ width, height }) => 220 * (height / width) + 'px'
|
|
||||||
|
|
||||||
// 检测是否包含中文
|
|
||||||
const containsChinese = (str) => /[\u4e00-\u9fa5]/.test(str)
|
|
||||||
|
|
||||||
// 打字中
|
|
||||||
function promptTyping(v) {
|
|
||||||
promptRecord[state.typeIndex] = v
|
|
||||||
}
|
|
||||||
function typeChange(index) {
|
|
||||||
state.params.prompt = promptRecord[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...toRefs(state),
|
|
||||||
open,
|
|
||||||
handleClose,
|
|
||||||
submit,
|
|
||||||
setDepend,
|
|
||||||
translation,
|
|
||||||
presuppose,
|
|
||||||
ratios,
|
|
||||||
getListData,
|
|
||||||
refreshList,
|
|
||||||
listRef,
|
|
||||||
del,
|
|
||||||
donwload,
|
|
||||||
exportToPage,
|
|
||||||
exportToImg,
|
|
||||||
getViewHeight,
|
|
||||||
selectFile,
|
|
||||||
promptTyping,
|
|
||||||
typeChange
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
:deep(.el-dialog__body) {
|
|
||||||
padding: 10px 24px 20px 24px !important;
|
|
||||||
}
|
|
||||||
:deep(.el-upload-dragger) {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
.sd-box {
|
|
||||||
height: calc(95vh - 120px);
|
|
||||||
display: flex;
|
|
||||||
.left {
|
|
||||||
position: relative;
|
|
||||||
width: 348px;
|
|
||||||
padding-right: 24px;
|
|
||||||
}
|
|
||||||
.left::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
height: 100%;
|
|
||||||
width: 2px;
|
|
||||||
background: #f0f1f4;
|
|
||||||
}
|
|
||||||
.input-wrap {
|
|
||||||
height: calc(100% - 67px);
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
.input-wrap::-webkit-scrollbar {
|
|
||||||
display: none; /* Chrome Safari */
|
|
||||||
}
|
|
||||||
.wrap {
|
|
||||||
position: relative;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin-top: 24px;
|
|
||||||
padding: 16px 14px;
|
|
||||||
background-color: rgb(243 244 249);
|
|
||||||
}
|
|
||||||
.wrap:first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
.wrap-title {
|
|
||||||
user-select: none;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
}
|
|
||||||
.presuppose-box {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
.p_select {
|
|
||||||
border: 2px solid var(--el-color-primary);
|
|
||||||
.img {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.presuppose {
|
|
||||||
padding: 2px;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
width: 90px;
|
|
||||||
height: 90px;
|
|
||||||
margin-bottom: 7px;
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
.cancel {
|
|
||||||
opacity: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.4);
|
|
||||||
color: #ffffff;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.cancel:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
.img {
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
opacity: 0.9;
|
|
||||||
background-size: 100%;
|
|
||||||
background-position: center center;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.img:hover {
|
|
||||||
transition: all 0.3s;
|
|
||||||
opacity: 1;
|
|
||||||
background-size: 120%;
|
|
||||||
// transform: scale3d(1.15, 1.15, 1);
|
|
||||||
}
|
|
||||||
.mask {
|
|
||||||
border-radius: 0 0 8px 8px;
|
|
||||||
position: absolute;
|
|
||||||
text-shadow: 0px 2px 2px rgba(0, 0, 0, 0.15);
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 24px;
|
|
||||||
text-align: center;
|
|
||||||
color: #ffffff;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 22px;
|
|
||||||
width: 100%;
|
|
||||||
backdrop-filter: blur(6px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.presuppose:nth-child(3n-2),
|
|
||||||
.presuppose:nth-child(3n-1) {
|
|
||||||
margin-right: 13px;
|
|
||||||
}
|
|
||||||
.images-box {
|
|
||||||
flex: 1;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
padding-left: 24px;
|
|
||||||
.box-header {
|
|
||||||
display: flex;
|
|
||||||
// padding-bottom: 0px;
|
|
||||||
.limit {
|
|
||||||
user-select: none;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.buttom-wrap {
|
|
||||||
width: 100%;
|
|
||||||
padding-top: 12px;
|
|
||||||
}
|
|
||||||
.mianze {
|
|
||||||
width: 100%;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
height: 22px;
|
|
||||||
span {
|
|
||||||
cursor: pointer;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-list {
|
|
||||||
height: calc(100% - 34px);
|
|
||||||
padding: 0;
|
|
||||||
margin: 12px 0;
|
|
||||||
list-style: none;
|
|
||||||
li {
|
|
||||||
padding-top: 20px;
|
|
||||||
}
|
|
||||||
li:first-child {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
.title {
|
|
||||||
user-select: none;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 12px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
.result-image {
|
|
||||||
width: 220px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.mini-text {
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uploader {
|
|
||||||
&__box {
|
|
||||||
position: relative;
|
|
||||||
padding: 30px 0;
|
|
||||||
color: #a8abb2;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.mask {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
backdrop-filter: blur(2px);
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
background: rgba(255, 255, 255, .6);
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
height: 240px;
|
|
||||||
width: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -3,7 +3,7 @@
|
|||||||
* @Date: 2022-02-13 22:18:35
|
* @Date: 2022-02-13 22:18:35
|
||||||
* @Description: 我的
|
* @Description: 我的
|
||||||
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
* @LastEditTime: 2024-03-11 01:42:44
|
* @LastEditTime: 2024-04-03 21:00:26
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
@ -13,9 +13,9 @@
|
|||||||
</el-tabs>
|
</el-tabs>
|
||||||
<div v-show="state.tabActiveName === 'pics'">
|
<div v-show="state.tabActiveName === 'pics'">
|
||||||
<uploader v-model="state.percent" class="upload" @done="uploadDone">
|
<uploader v-model="state.percent" class="upload" @done="uploadDone">
|
||||||
<el-button class="upload-btn" plain>上传图片 <i class="iconfont icon-upload" /></el-button>
|
<el-button class="upload-btn" plain><i class="iconfont icon-upload" /> 上传图片</el-button>
|
||||||
</uploader>
|
</uploader>
|
||||||
<el-button class="upload-btn upload-psd" plain type="primary" @click="openPSD">上传 PSD 模板</el-button>
|
<el-button class="upload-btn upload-psd" plain type="primary" @click="openPSD">导入 PSD</el-button>
|
||||||
<div style="margin: 1rem; height: 100vh">
|
<div style="margin: 1rem; height: 100vh">
|
||||||
<photo-list
|
<photo-list
|
||||||
ref="imgListRef"
|
ref="imgListRef"
|
||||||
@ -294,11 +294,11 @@ defineExpose({
|
|||||||
margin: 0 0 0 1rem;
|
margin: 0 0 0 1rem;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
&-btn {
|
&-btn {
|
||||||
width: 160px;
|
width: 170px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
&-psd {
|
&-psd {
|
||||||
width: 124px;
|
width: 114px;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ export default {
|
|||||||
IMG_URL: 'https://store.palxp.cn/', // 七牛云资源地址
|
IMG_URL: 'https://store.palxp.cn/', // 七牛云资源地址
|
||||||
// ICONFONT_URL: '//at.alicdn.com/t/font_3223711_74mlzj4jdue.css',
|
// ICONFONT_URL: '//at.alicdn.com/t/font_3223711_74mlzj4jdue.css',
|
||||||
ICONFONT_URL: '//at.alicdn.com/t/font_2717063_ypy8vprc3b.css?display=swap',
|
ICONFONT_URL: '//at.alicdn.com/t/font_2717063_ypy8vprc3b.css?display=swap',
|
||||||
ICONFONT_EXTRA: '//at.alicdn.com/t/c/font_3228074_42xym3extur.css',
|
ICONFONT_EXTRA: '//at.alicdn.com/t/c/font_3228074_8r5ffak8d5q.css',
|
||||||
QINIUYUN_PLUGIN: 'https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/qiniu-js/2.5.5/qiniu.min.js',
|
QINIUYUN_PLUGIN: 'https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/qiniu-js/2.5.5/qiniu.min.js',
|
||||||
supportSubFont: true, // 是否开启服务端字体压缩
|
supportSubFont: true, // 是否开启服务端字体压缩
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* @Date: 2022-03-09 14:20:09
|
* @Date: 2022-03-09 14:20:09
|
||||||
* @Description: 处理常用操作
|
* @Description: 处理常用操作
|
||||||
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
* @LastEditTime: 2023-11-30 10:09:55
|
* @LastEditTime: 2024-04-04 00:33:01
|
||||||
*/
|
*/
|
||||||
import { useControlStore, useWidgetStore } from '@/store'
|
import { useControlStore, useWidgetStore } from '@/store'
|
||||||
import { TdWidgetData } from '@/store/design/widget'
|
import { TdWidgetData } from '@/store/design/widget'
|
||||||
@ -13,19 +13,19 @@ const controlStore = useControlStore()
|
|||||||
const widgetStore = useWidgetStore()
|
const widgetStore = useWidgetStore()
|
||||||
|
|
||||||
export default function keyCodeOptions(e: any, params: any) {
|
export default function keyCodeOptions(e: any, params: any) {
|
||||||
const { f } = params
|
const { range } = params
|
||||||
switch (e.keyCode) {
|
switch (e.keyCode) {
|
||||||
case 38:
|
case 38:
|
||||||
udlr('top', -1 * f, e)
|
udlr('top', -1 * range, e)
|
||||||
break
|
break
|
||||||
case 40:
|
case 40:
|
||||||
udlr('top', Number(f), e)
|
udlr('top', Number(range), e)
|
||||||
break
|
break
|
||||||
case 37:
|
case 37:
|
||||||
udlr('left', -1 * f, e)
|
udlr('left', -1 * range, e)
|
||||||
break
|
break
|
||||||
case 39:
|
case 39:
|
||||||
udlr('left', Number(f), e)
|
udlr('left', Number(range), e)
|
||||||
break
|
break
|
||||||
case 46:
|
case 46:
|
||||||
case 8:
|
case 8:
|
||||||
|
70
src/mixins/scKeyCodes.ts
Normal file
70
src/mixins/scKeyCodes.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* @Author: ShawnPhang
|
||||||
|
* @Date: 2024-04-04 00:36:13
|
||||||
|
* @Description: 快捷键支持列表
|
||||||
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
|
* @LastEditTime: 2024-04-05 05:56:27
|
||||||
|
*/
|
||||||
|
const ctrlKey = isMacOS() ? `⌘` : `Ctrl`
|
||||||
|
function isMacOS() {
|
||||||
|
return navigator.userAgent.includes(`Macintosh`) || navigator.userAgent.includes(`Mac OS X`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
feat: `拖拽画布`,
|
||||||
|
info: `空格 + 鼠标拖拽`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
feat: `画布缩小`,
|
||||||
|
info: `${ctrlKey} - / ${ctrlKey} + 滚轮`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
feat: `画布放大`,
|
||||||
|
info: `${ctrlKey} + / ${ctrlKey} + 滚轮`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
feat: `保存`,
|
||||||
|
info: `${ctrlKey} + S`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
feat: `撤销`,
|
||||||
|
info: `${ctrlKey} + Z`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
feat: `重做`,
|
||||||
|
info: `${ctrlKey} + Shift + Z`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
feat: `元素移动`,
|
||||||
|
info: `← ↑ → ↓`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
feat: `快速移动`,
|
||||||
|
info: `Shift + ← ↑ → ↓`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
feat: `复制`,
|
||||||
|
info: `${ctrlKey} + C`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
feat: `粘贴`,
|
||||||
|
info: `${ctrlKey} + V`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
feat: `删除`,
|
||||||
|
info: `Delete / Backspace`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
feat: `多选`,
|
||||||
|
info: `${ctrlKey} / Shift + 点选`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
feat: `成组`,
|
||||||
|
info: `${ctrlKey} + G`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
feat: `取消选中`,
|
||||||
|
info: `ESC`,
|
||||||
|
},
|
||||||
|
]
|
@ -3,12 +3,8 @@
|
|||||||
* @Date: 2021-08-01 14:12:08
|
* @Date: 2021-08-01 14:12:08
|
||||||
* @Description: 快捷键,目前是mixin形式放入views/index.vue中
|
* @Description: 快捷键,目前是mixin形式放入views/index.vue中
|
||||||
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
* @LastEditTime: 2023-09-19 17:29:06
|
* @LastEditTime: 2024-04-04 00:36:19
|
||||||
*/
|
*/
|
||||||
// import store from '@/store'
|
|
||||||
// const _this: any = {}
|
|
||||||
// _this.dHistoryParams = store.getters.dHistoryParams
|
|
||||||
|
|
||||||
import keyCodeOptions from './methods/keyCodeOptions'
|
import keyCodeOptions from './methods/keyCodeOptions'
|
||||||
import dealWithCtrl from './methods/dealWithCtrl'
|
import dealWithCtrl from './methods/dealWithCtrl'
|
||||||
import { TControlStore } from '@/store/design/control'
|
import { TControlStore } from '@/store/design/control'
|
||||||
@ -16,21 +12,6 @@ import { useControlStore, useWidgetStore } from '@/store'
|
|||||||
|
|
||||||
const ignoreNode = ['INPUT', 'TEXTAREA']
|
const ignoreNode = ['INPUT', 'TEXTAREA']
|
||||||
|
|
||||||
// 系统组合键
|
|
||||||
const systemKeyCode = [
|
|
||||||
{
|
|
||||||
// ctrl+r刷新
|
|
||||||
key: ['ctrlKey', 'metaKey'],
|
|
||||||
code: 82,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// ctrl+alt+i打开开发者
|
|
||||||
key: ['ctrlKey', 'metaKey'],
|
|
||||||
key2: ['altKey'],
|
|
||||||
code: 73,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
let hadDown = false
|
let hadDown = false
|
||||||
const appContainer: any = document.querySelector('#app')
|
const appContainer: any = document.querySelector('#app')
|
||||||
const controlStore = useControlStore()
|
const controlStore = useControlStore()
|
||||||
@ -53,11 +34,7 @@ const shortcuts = {
|
|||||||
const ctrl = e.key === 'Control' || e.key === 'Meta'
|
const ctrl = e.key === 'Control' || e.key === 'Meta'
|
||||||
const alt = e.key === 'Alt'
|
const alt = e.key === 'Alt'
|
||||||
const shift = e.key === 'Shift'
|
const shift = e.key === 'Shift'
|
||||||
// const dir = e.keyCode === 37 || e.keyCode === 38 || e.keyCode === 39 || e.keyCode === 40
|
|
||||||
// const specialKey = ctrl || alt || shift || dir
|
|
||||||
// if (specialKey || e.metaKey) {
|
|
||||||
// hadDown = false
|
|
||||||
// }
|
|
||||||
if (shift || ctrl) {
|
if (shift || ctrl) {
|
||||||
store.updateAltDown(true)
|
store.updateAltDown(true)
|
||||||
// store.dispatch('updateAltDown', true)
|
// store.dispatch('updateAltDown', true)
|
||||||
@ -72,28 +49,6 @@ const shortcuts = {
|
|||||||
}
|
}
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
// const systemKey = systemKeyCode.find((item) => {
|
|
||||||
// let f = false
|
|
||||||
// let f2 = false
|
|
||||||
// for (let i = 0; i < item.key.length; ++i) {
|
|
||||||
// f = e[item.key[i]]
|
|
||||||
// if (f) {
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (item.key2) {
|
|
||||||
// for (let i = 0; i < item.key2.length; ++i) {
|
|
||||||
// f2 = e[item.key2[i]]
|
|
||||||
// if (f2) {
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return f && f2 && e.keyCode === item.code
|
|
||||||
// })
|
|
||||||
// if (systemKey) {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
const withCtrl = e.ctrlKey || e.metaKey
|
const withCtrl = e.ctrlKey || e.metaKey
|
||||||
if (withCtrl && !(ctrl || alt || shift)) {
|
if (withCtrl && !(ctrl || alt || shift)) {
|
||||||
dealCtrl(e, instance)
|
dealCtrl(e, instance)
|
||||||
@ -104,20 +59,9 @@ const shortcuts = {
|
|||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
const withShift = e.shiftKey
|
const withShift = e.shiftKey
|
||||||
// if (withShift && !specialKey) {
|
|
||||||
// return
|
const range = withShift ? 10 : 1
|
||||||
// }
|
keyCodeOptions(e, { range })
|
||||||
// // TODO
|
|
||||||
// if (!this.dActiveElement) {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// if (this.dActiveElement.uuid === '-1') {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// e.stopPropagation()
|
|
||||||
// e.preventDefault()
|
|
||||||
const f = withShift ? 10 : 1
|
|
||||||
keyCodeOptions(e, { f })
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleKeyup(store: TControlStore, checkCtrl: number | undefined) {
|
handleKeyup(store: TControlStore, checkCtrl: number | undefined) {
|
||||||
|
@ -1,29 +1,25 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: ShawnPhang
|
* @Author: ShawnPhang
|
||||||
* @Date: 2021-07-13 02:48:38
|
* @Date: 2021-07-13 02:48:38
|
||||||
* @Description: 本地测试项目请勿修改此文件
|
* @Description: 本地测试用户身份写死
|
||||||
* @LastEditors: ShawnPhang <https://m.palxp.cn>, Jeremy Yu <https://github.com/JeremyYu-cn>
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
* @LastEditTime: 2024-02-26 17:54:00
|
* @LastEditTime: 2024-04-03 20:56:23
|
||||||
*/
|
*/
|
||||||
import axios, { AxiosRequestConfig, AxiosResponse, AxiosStatic } from 'axios'
|
import axios, { AxiosRequestConfig, AxiosResponse, AxiosStatic } from 'axios'
|
||||||
// import store from '@/store'
|
|
||||||
import app_config, { LocalStorageKey } from '@/config'
|
import app_config, { LocalStorageKey } from '@/config'
|
||||||
import { useBaseStore, useUserStore } from '@/store/index';
|
import { useBaseStore, useUserStore } from '@/store/index';
|
||||||
|
|
||||||
axios.defaults.timeout = 30000
|
axios.defaults.timeout = 30000
|
||||||
axios.defaults.headers.authorization = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTAwMDEsImV4cCI6MTc4ODU3NDc1MDU4NX0.L_t6DFD48Dm6rUPfgIgOWJkz18En1m_-hhMHcpbxliY';
|
// axios.defaults.headers.authorization = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTAwMDEsImV4cCI6MTc4ODU3NDc1MDU4NX0.L_t6DFD48Dm6rUPfgIgOWJkz18En1m_-hhMHcpbxliY';
|
||||||
|
const defaultToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTAwMDEsImV4cCI6MTc4ODU3NDc1MDU4NX0.L_t6DFD48Dm6rUPfgIgOWJkz18En1m_-hhMHcpbxliY';
|
||||||
// const version = app_config.VERSION;
|
// const version = app_config.VERSION;
|
||||||
const baseUrl = app_config.API_URL
|
const baseUrl = app_config.API_URL
|
||||||
|
|
||||||
// 请求拦截器
|
// 请求拦截器
|
||||||
axios.interceptors.request.use(
|
axios.interceptors.request.use(
|
||||||
(config: AxiosRequestConfig) => {
|
(config: AxiosRequestConfig) => {
|
||||||
// const access_token = store.state.currentUser.access_token;
|
|
||||||
const url = config.url ?? ""
|
const url = config.url ?? ""
|
||||||
const values = {}
|
const values = {}
|
||||||
// values.access_token = access_token;
|
|
||||||
// values.version = version;
|
|
||||||
|
|
||||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||||
config.url = url.startsWith('/') ? baseUrl + url : config.url = baseUrl + '/' + url
|
config.url = url.startsWith('/') ? baseUrl + url : config.url = baseUrl + '/' + url
|
||||||
}
|
}
|
||||||
@ -48,7 +44,6 @@ axios.interceptors.request.use(
|
|||||||
axios.interceptors.response.use((res: AxiosResponse<any>) => {
|
axios.interceptors.response.use((res: AxiosResponse<any>) => {
|
||||||
// store.dispatch('hideLoading');
|
// store.dispatch('hideLoading');
|
||||||
// 接口规则:只有正确code为200时返回result结果对象,错误返回整个结果对象
|
// 接口规则:只有正确code为200时返回result结果对象,错误返回整个结果对象
|
||||||
|
|
||||||
if (!res.data) {
|
if (!res.data) {
|
||||||
return Promise.reject(res)
|
return Promise.reject(res)
|
||||||
}
|
}
|
||||||
@ -95,9 +90,9 @@ const fetch = <T = any> (
|
|||||||
// store.commit('loading', '加载中..');
|
// store.commit('loading', '加载中..');
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = localStorage.getItem(LocalStorageKey.tokenKey)
|
const token = defaultToken//localStorage.getItem(LocalStorageKey.tokenKey)
|
||||||
const headerObject: Record<string, any> = {}
|
const headerObject: Record<string, any> = {}
|
||||||
token && (headerObject.authorization = token)
|
token && (headerObject.Authorization = token)
|
||||||
|
|
||||||
if (type === 'get') {
|
if (type === 'get') {
|
||||||
return axios.get(url, {
|
return axios.get(url, {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* @Description:
|
* @Description:
|
||||||
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
* @LastUpdateContent: Support typescript
|
* @LastUpdateContent: Support typescript
|
||||||
* @LastEditTime: 2024-04-03 10:58:42
|
* @LastEditTime: 2024-04-05 05:34:43
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div id="page-design-index" ref="pageDesignIndex" class="page-design-bg-color">
|
<div id="page-design-index" ref="pageDesignIndex" class="page-design-bg-color">
|
||||||
@ -16,22 +16,29 @@
|
|||||||
<div :class="['operation-item', { disable: !undoable }]" @click="undoable ? handleHistory('undo') : ''"><i class="iconfont icon-undo" /></div>
|
<div :class="['operation-item', { disable: !undoable }]" @click="undoable ? handleHistory('undo') : ''"><i class="iconfont icon-undo" /></div>
|
||||||
<div :class="['operation-item', { disable: !redoable }]" @click="redoable ? handleHistory('redo') : ''"><i class="iconfont icon-redo" /></div>
|
<div :class="['operation-item', { disable: !redoable }]" @click="redoable ? handleHistory('redo') : ''"><i class="iconfont icon-redo" /></div>
|
||||||
</div>
|
</div>
|
||||||
<el-tooltip effect="dark" content="标尺" placement="bottom">
|
<el-divider direction="vertical" />
|
||||||
<i style="font-size: 20px" class="icon sd-biaochi extra-operation" @click="changeLineGuides" />
|
<Folder @select="dealWith" ref="ref1"> <div class="operation-item"><i class="icon sd-wenjian" /> <span class="text" >文件</span></div> </Folder>
|
||||||
</el-tooltip>
|
<Helper @select="dealWith"> <div class="operation-item"><i class="icon sd-bangzhu" /> <span class="text" >帮助</span></div> </Helper>
|
||||||
|
<!-- <el-tooltip effect="dark" :show-after="300" :offset="0" content="标尺" placement="bottom">
|
||||||
|
<i style="font-size: 20px" class="icon sd-biaochi operation-item" @click="changeLineGuides" />
|
||||||
|
</el-tooltip> -->
|
||||||
|
<el-divider direction="vertical" />
|
||||||
</div>
|
</div>
|
||||||
<HeaderOptions ref="optionsRef" v-model="state.isContinue" @change="optionsChange" />
|
<HeaderOptions ref="optionsRef" v-model="state.isContinue" @change="optionsChange">
|
||||||
|
<el-button size="large" class="primary-btn" @click="dealWith('save')">保存</el-button>
|
||||||
|
<el-button ref="ref4" size="large" class="primary-btn" plain type="primary" @click="dealWith('download')">下载作品</el-button>
|
||||||
|
</HeaderOptions>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="page-design-index-wrap">
|
<div class="page-design-index-wrap">
|
||||||
<widget-panel></widget-panel>
|
<widget-panel ref="ref2"></widget-panel>
|
||||||
<design-board class="page-design-wrap" pageDesignCanvasId="page-design-canvas">
|
<design-board class="page-design-wrap" pageDesignCanvasId="page-design-canvas">
|
||||||
<!-- 用于挡住画布溢出部分,因为使用overflow有bug -->
|
<!-- 用于挡住画布溢出部分,因为使用overflow有bug -->
|
||||||
<div class="shelter" :style="{ width: Math.floor((dPage.width * dZoom) / 100) + 'px', height: Math.floor((dPage.height * dZoom) / 100) + 'px' }"></div>
|
<div class="shelter" :style="{ width: Math.floor((dPage.width * dZoom) / 100) + 'px', height: Math.floor((dPage.height * dZoom) / 100) + 'px' }"></div>
|
||||||
<!-- 提供一个背景图层 -->
|
<!-- 提供一个背景图层 -->
|
||||||
<div class="shelter-bg transparent-bg" :style="{ width: Math.floor((dPage.width * dZoom) / 100) + 'px', height: Math.floor((dPage.height * dZoom) / 100) + 'px' }"></div>
|
<div class="shelter-bg transparent-bg" :style="{ width: Math.floor((dPage.width * dZoom) / 100) + 'px', height: Math.floor((dPage.height * dZoom) / 100) + 'px' }"></div>
|
||||||
</design-board>
|
</design-board>
|
||||||
<style-panel></style-panel>
|
<style-panel ref="ref3"></style-panel>
|
||||||
</div>
|
</div>
|
||||||
<!-- 标尺 -->
|
<!-- 标尺 -->
|
||||||
<line-guides :show="state.showLineGuides" />
|
<line-guides :show="state.showLineGuides" />
|
||||||
@ -50,6 +57,8 @@
|
|||||||
@cancel="downloadCancel"
|
@cancel="downloadCancel"
|
||||||
@done="state.downloadPercent = 0"
|
@done="state.downloadPercent = 0"
|
||||||
/>
|
/>
|
||||||
|
<!-- 漫游导航 -->
|
||||||
|
<Tour ref="tourRef" :steps="[ref1, ref2, ref3, ref4]" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -67,12 +76,21 @@ import lineGuides from '@/components/modules/layout/lineGuides.vue'
|
|||||||
import shortcuts from '@/mixins/shortcuts'
|
import shortcuts from '@/mixins/shortcuts'
|
||||||
// import wGroup from '@/components/modules/widgets/wGroup/wGroup.vue'
|
// import wGroup from '@/components/modules/widgets/wGroup/wGroup.vue'
|
||||||
import HeaderOptions from './components/HeaderOptions.vue'
|
import HeaderOptions from './components/HeaderOptions.vue'
|
||||||
|
import Folder from './components/Folder.vue'
|
||||||
|
import Helper from './components/Helper.vue'
|
||||||
import ProgressLoading from '@/components/common/ProgressLoading/download.vue'
|
import ProgressLoading from '@/components/common/ProgressLoading/download.vue'
|
||||||
// import { useSetupMapGetters } from '@/common/hooks/mapGetters'
|
// import { useSetupMapGetters } from '@/common/hooks/mapGetters'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { wGroupSetting } from '@/components/modules/widgets/wGroup/groupSetting'
|
import { wGroupSetting } from '@/components/modules/widgets/wGroup/groupSetting'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useCanvasStore, useControlStore, useHistoryStore, useWidgetStore, useGroupStore } from '@/store'
|
import { useCanvasStore, useControlStore, useHistoryStore, useWidgetStore, useGroupStore } from '@/store'
|
||||||
|
import type { ButtonInstance } from 'element-plus'
|
||||||
|
import Tour from './components/Tour.vue'
|
||||||
|
|
||||||
|
const ref1 = ref<ButtonInstance>()
|
||||||
|
const ref2 = ref<ButtonInstance>()
|
||||||
|
const ref3 = ref<ButtonInstance>()
|
||||||
|
const ref4 = ref<ButtonInstance>()
|
||||||
|
|
||||||
type TState = {
|
type TState = {
|
||||||
style: CSSProperties
|
style: CSSProperties
|
||||||
@ -93,8 +111,7 @@ const groupStore = useGroupStore()
|
|||||||
const { dPage } = storeToRefs(useCanvasStore())
|
const { dPage } = storeToRefs(useCanvasStore())
|
||||||
const { dZoom } = storeToRefs(useCanvasStore())
|
const { dZoom } = storeToRefs(useCanvasStore())
|
||||||
const { dHistoryParams } = storeToRefs(useHistoryStore())
|
const { dHistoryParams } = storeToRefs(useHistoryStore())
|
||||||
const { dActiveElement, dCopyElement } = storeToRefs(widgetStore)
|
// const { dActiveElement, dCopyElement } = storeToRefs(widgetStore)
|
||||||
|
|
||||||
|
|
||||||
const state = reactive<TState>({
|
const state = reactive<TState>({
|
||||||
style: {
|
style: {
|
||||||
@ -129,10 +146,6 @@ function jump2home() {
|
|||||||
window.open('https://xp.palxp.cn/')
|
window.open('https://xp.palxp.cn/')
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
jump2home,
|
|
||||||
})
|
|
||||||
|
|
||||||
const undoable = computed(() => {
|
const undoable = computed(() => {
|
||||||
return !(
|
return !(
|
||||||
dHistoryParams.value.index === -1 ||
|
dHistoryParams.value.index === -1 ||
|
||||||
@ -216,15 +229,32 @@ function fixTopBarScroll() {
|
|||||||
state.style.left = `-${scrollLeft}px`
|
state.style.left = `-${scrollLeft}px`
|
||||||
}
|
}
|
||||||
|
|
||||||
// function clickListener(e: Event) {
|
|
||||||
// console.log('click listener', e)
|
|
||||||
// }
|
|
||||||
|
|
||||||
function optionsChange({ downloadPercent, downloadText, downloadMsg }: { downloadPercent: number, downloadText: string, downloadMsg?: string }) {
|
function optionsChange({ downloadPercent, downloadText, downloadMsg }: { downloadPercent: number, downloadText: string, downloadMsg?: string }) {
|
||||||
state.downloadPercent = downloadPercent
|
state.downloadPercent = downloadPercent
|
||||||
state.downloadText = downloadText
|
state.downloadText = downloadText
|
||||||
state.downloadMsg = downloadMsg
|
state.downloadMsg = downloadMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tourRef = ref<any>()
|
||||||
|
const fns: any = {
|
||||||
|
openTour: () => {
|
||||||
|
tourRef.value.open()
|
||||||
|
},
|
||||||
|
save: () => {
|
||||||
|
optionsRef.value?.save(false)
|
||||||
|
},
|
||||||
|
download: () => {
|
||||||
|
optionsRef.value?.download()
|
||||||
|
},
|
||||||
|
changeLineGuides
|
||||||
|
}
|
||||||
|
const dealWith = (fnName: string, params?: any) => {
|
||||||
|
fns[fnName](params)
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
jump2home,
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* @Date: 2022-01-10 14:57:53
|
* @Date: 2022-01-10 14:57:53
|
||||||
* @Description: Psd文件解析
|
* @Description: Psd文件解析
|
||||||
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
* @LastEditTime: 2023-10-13 00:12:11
|
* @LastEditTime: 2024-04-03 20:58:43
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div id="page-design-index" ref="pageDesignIndex">
|
<div id="page-design-index" ref="pageDesignIndex">
|
||||||
|
43
src/views/components/Folder.vue
Normal file
43
src/views/components/Folder.vue
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<!--
|
||||||
|
* @Author: ShawnPhang
|
||||||
|
* @Date: 2024-04-03 19:15:21
|
||||||
|
* @Description: 文件
|
||||||
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
|
* @LastEditTime: 2024-04-05 06:01:20
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<el-dropdown trigger="click" size="large" placement="bottom-start">
|
||||||
|
<span class="el-dropdown-link">
|
||||||
|
<slot />
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item><div class="item">创建设计</div></el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="openPSD">导入文件</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="$emit('select', 'save')" divided>保存</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="$emit('select', 'download')">导出文件</el-dropdown-item>
|
||||||
|
<el-dropdown-item disabled>版本记录</el-dropdown-item>
|
||||||
|
<el-dropdown-item disabled>批量套模板</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="$emit('select', 'changeLineGuides')" divided>标尺与参考线</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
// import { ref } from 'vue'
|
||||||
|
import { useRouter} from 'vue-router'
|
||||||
|
import { ElDropdown, ElDropdownItem, ElDropdownMenu } from 'element-plus'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const openPSD = () => {
|
||||||
|
window.open(router.resolve('/psd').href, '_blank')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.item {
|
||||||
|
width: 224px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -3,7 +3,7 @@
|
|||||||
* @Date: 2022-01-12 11:26:53
|
* @Date: 2022-01-12 11:26:53
|
||||||
* @Description: 顶部操作按钮组
|
* @Description: 顶部操作按钮组
|
||||||
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
* @LastEditTime: 2024-04-03 12:21:25
|
* @LastEditTime: 2024-04-05 05:37:53
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div class="top-title"><el-input v-model="state.title" placeholder="未命名的设计" class="input-wrap" /></div>
|
<div class="top-title"><el-input v-model="state.title" placeholder="未命名的设计" class="input-wrap" /></div>
|
||||||
@ -17,10 +17,10 @@
|
|||||||
<div class="divide__line">|</div>
|
<div class="divide__line">|</div>
|
||||||
</template>
|
</template>
|
||||||
<!-- <el-button @click="draw">绘制(测试)</el-button> -->
|
<!-- <el-button @click="draw">绘制(测试)</el-button> -->
|
||||||
<el-button size="large" class="primary-btn" :disabled="tempEditing" @click="save(false)">保存</el-button>
|
<!-- <copyRight> -->
|
||||||
<copyRight>
|
<slot />
|
||||||
<el-button :loading="state.loading" size="large" class="primary-btn" :disabled="tempEditing" plain type="primary" @click="download">下载作品</el-button>
|
<!-- <el-button :loading="state.loading" size="large" class="primary-btn" :disabled="tempEditing" plain type="primary" @click="download">下载作品</el-button> -->
|
||||||
</copyRight>
|
<!-- </copyRight> -->
|
||||||
</div>
|
</div>
|
||||||
<!-- 生成图片组件 -->
|
<!-- 生成图片组件 -->
|
||||||
<SaveImage ref="canvasImage" />
|
<SaveImage ref="canvasImage" />
|
||||||
@ -34,7 +34,7 @@ import _dl from '@/common/methods/download'
|
|||||||
import useNotification from '@/common/methods/notification'
|
import useNotification from '@/common/methods/notification'
|
||||||
import SaveImage from '@/components/business/save-download/CreateCover.vue'
|
import SaveImage from '@/components/business/save-download/CreateCover.vue'
|
||||||
import { useFontStore } from '@/common/methods/fonts'
|
import { useFontStore } from '@/common/methods/fonts'
|
||||||
import copyRight from './CopyRight.vue'
|
// import copyRight from './CopyRight.vue'
|
||||||
import _config from '@/config'
|
import _config from '@/config'
|
||||||
import useConfirm from '@/common/methods/confirm'
|
import useConfirm from '@/common/methods/confirm'
|
||||||
import { useControlStore, useHistoryStore, useCanvasStore, useUserStore, useWidgetStore } from '@/store/index'
|
import { useControlStore, useHistoryStore, useCanvasStore, useUserStore, useWidgetStore } from '@/store/index'
|
||||||
@ -136,6 +136,7 @@ async function saveTemp() {
|
|||||||
}
|
}
|
||||||
async function download() {
|
async function download() {
|
||||||
if (state.loading === true) {
|
if (state.loading === true) {
|
||||||
|
useNotification('作品导出中', '当前有作品正在导出,请稍候再试')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 临时提示
|
// 临时提示
|
||||||
@ -173,7 +174,11 @@ async function saveTemp() {
|
|||||||
state.loading = false
|
state.loading = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
emit('change', { downloadPercent: 100, downloadText: '作品下载成功', downloadMsg: '仅供学习、研究或欣赏等用途,暂不提供商业授权。' })
|
emit('change', { downloadPercent: 100, downloadText: '作品下载成功', downloadMsg: '该作品仅供学习、研究或欣赏等用途,暂不提供商业授权。' })
|
||||||
|
state.loading = false
|
||||||
|
} else {
|
||||||
|
emit('change', { downloadPercent: 0, downloadText: '请稍候..' })
|
||||||
|
useNotification('作品为空', '无法下载,请先创建设计', { type: 'error' })
|
||||||
state.loading = false
|
state.loading = false
|
||||||
}
|
}
|
||||||
}, 100)
|
}, 100)
|
||||||
@ -257,9 +262,10 @@ defineExpose({
|
|||||||
.top-title {
|
.top-title {
|
||||||
color: @color-black;
|
color: @color-black;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding-left: 88px;
|
padding-left: 20px;
|
||||||
// font-weight: bold;
|
// font-weight: bold;
|
||||||
.input-wrap {
|
.input-wrap {
|
||||||
|
// box-shadow: none;
|
||||||
width: 15rem;
|
width: 15rem;
|
||||||
:deep(input) {
|
:deep(input) {
|
||||||
border-color: #ffffff;
|
border-color: #ffffff;
|
||||||
@ -268,7 +274,7 @@ defineExpose({
|
|||||||
}
|
}
|
||||||
.input-wrap:hover {
|
.input-wrap:hover {
|
||||||
:deep(input) {
|
:deep(input) {
|
||||||
border-color: #e8eaec;
|
// border-color: #e8eaec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,7 +283,6 @@ defineExpose({
|
|||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.divide__line {
|
.divide__line {
|
||||||
margin: 0 1rem;
|
margin: 0 1rem;
|
||||||
color: #e8eaec;
|
color: #e8eaec;
|
||||||
|
94
src/views/components/Helper.vue
Normal file
94
src/views/components/Helper.vue
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<!--
|
||||||
|
* @Author: ShawnPhang
|
||||||
|
* @Date: 2024-04-03 19:15:21
|
||||||
|
* @Description: 文件
|
||||||
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
|
* @LastEditTime: 2024-04-05 04:46:03
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<el-dropdown ref="dropdownRef" max-height="70vh" :hide-on-click="false" trigger="click" size="large" placement="bottom-start">
|
||||||
|
<span class="el-dropdown-link">
|
||||||
|
<slot />
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<div v-show="type === 'shortkey'" class="help-list">
|
||||||
|
<div @click="type = 'menu'" class="back">
|
||||||
|
<span class="icon-box"><i class="iconfont icon-right"></i></span> <b>快捷键</b>
|
||||||
|
</div>
|
||||||
|
<el-divider />
|
||||||
|
<div v-for="(sc, si) in scKeyCodes" :key="'sc' + si" class="item">
|
||||||
|
<span class="title">{{ sc.feat }}</span
|
||||||
|
><span class="instruct">{{ sc.info }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-dropdown-menu v-show="type === 'menu'">
|
||||||
|
<el-dropdown-item @click="type = 'shortkey'">快捷键</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="openTour">新手引导</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="openIssues"><div class="menu-item">反馈或建议</div></el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { ElDropdown, ElDropdownItem, ElDropdownMenu } from 'element-plus'
|
||||||
|
import scKeyCodes from '@/mixins/scKeyCodes'
|
||||||
|
const emit = defineEmits()
|
||||||
|
|
||||||
|
const type = ref('menu')
|
||||||
|
const dropdownRef = ref()
|
||||||
|
|
||||||
|
const openTour = () => {
|
||||||
|
emit('select', 'openTour')
|
||||||
|
dropdownRef.value?.handleClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
const openIssues = () => {
|
||||||
|
window.open('https://github.com/palxiao/poster-design/issues', '_blank')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
:deep(.el-divider--horizontal) {
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
.menu-item {
|
||||||
|
width: 170px;
|
||||||
|
}
|
||||||
|
.help-list {
|
||||||
|
user-select: none;
|
||||||
|
.back {
|
||||||
|
margin-top: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
padding: 8px 16px 0 6px;
|
||||||
|
.icon-box {
|
||||||
|
display: inline-block;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.item {
|
||||||
|
width: 240px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px 16px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
color: #4c535c;
|
||||||
|
white-space: nowrap;
|
||||||
|
.title {
|
||||||
|
color: #333333;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.instruct {
|
||||||
|
padding: 7px 8px;
|
||||||
|
margin-left: 14px;
|
||||||
|
color: #222529;
|
||||||
|
background: #f1f2f4;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
40
src/views/components/Tour.vue
Normal file
40
src/views/components/Tour.vue
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<!--
|
||||||
|
* @Author: ShawnPhang
|
||||||
|
* @Date: 2024-04-04 03:05:45
|
||||||
|
* @Description: 漫游导航
|
||||||
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
|
* @LastEditTime: 2024-04-05 05:31:01
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<el-tour v-model="isShow">
|
||||||
|
<el-tour-step :target="steps[0]?.$el" title="文件管理">
|
||||||
|
<div>点击文件菜单,管理你的设计,设置页面视图等操作。</div>
|
||||||
|
</el-tour-step>
|
||||||
|
<el-tour-step placement="right" :target="steps[1]?.$el" title="左侧工具栏" description="在这里可以选择模板开始设计,或是挑选文字、图片等素材拖拽至画布中。" />
|
||||||
|
<el-tour-step placement="left" :target="steps[2]?.$el" title="右侧属性栏" description="当选中画布中的元素时,在此处会显示相应的编辑界面;同时也可以切换到“图层”管理。" />
|
||||||
|
<el-tour-step :target="steps[3]?.$el" title="下载作品" description="点击此处即可导出当前作品,赶紧试试吧。" />
|
||||||
|
</el-tour>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ElTour, ElTourStep } from 'element-plus'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
type TProps = {
|
||||||
|
steps: any,
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<TProps>(), {
|
||||||
|
steps: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const isShow = ref(false)
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
isShow.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
open
|
||||||
|
})
|
||||||
|
</script>
|
@ -2,8 +2,8 @@
|
|||||||
* @Author: ShawnPhang
|
* @Author: ShawnPhang
|
||||||
* @Date: 2022-07-12 11:26:53
|
* @Date: 2022-07-12 11:26:53
|
||||||
* @Description: 上传用户模板
|
* @Description: 上传用户模板
|
||||||
* @LastEditors: ShawnPhang <site: book.palxp.com>
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
* @LastEditTime: 2023-07-14 09:17:56
|
* @LastEditTime: 2024-04-03 20:57:29
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<el-button v-show="isDone" type="primary" plain @click="prepare"><b>上传模板</b></el-button>
|
<el-button v-show="isDone" type="primary" plain @click="prepare"><b>上传模板</b></el-button>
|
||||||
@ -136,7 +136,7 @@ async function uploadImgs() {
|
|||||||
const { id, stat, msg } = await api.home.saveWorks({ cover, title: '自设计模板', data: JSON.stringify({ page, widgets }), width: page.width, height: page.height })
|
const { id, stat, msg } = await api.home.saveWorks({ cover, title: '自设计模板', data: JSON.stringify({ page, widgets }), width: page.width, height: page.height })
|
||||||
stat !== 0 ? useNotification('保存成功', '可在"我的模板"中查看') : useNotification('保存失败', msg, { type: 'error' })
|
stat !== 0 ? useNotification('保存成功', '可在"我的模板"中查看') : useNotification('保存失败', msg, { type: 'error' })
|
||||||
router.push({ path: '/psd', query: { id }, replace: true })
|
router.push({ path: '/psd', query: { id }, replace: true })
|
||||||
emit('change', { downloadPercent: 99.99, downloadText: '上传完成', cancelText: '查看我的作品' }) // 关闭弹窗
|
emit('change', { downloadPercent: 99.99, downloadText: '上传完成', cancelText: '点击查看作品' }) // 关闭弹窗
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user