parent
7ded80703a
commit
bc96eaa91a
20 changed files with 1297 additions and 5 deletions
@ -0,0 +1,8 @@ |
||||
// to fixed https://github.com/ElemeFE/element/issues/2461 |
||||
.el-dialog { |
||||
transform: none; |
||||
left: 0; |
||||
position: relative; |
||||
margin: 0 auto; |
||||
} |
||||
|
@ -0,0 +1,16 @@ |
||||
/** |
||||
* 求出多个数组的最小长度 |
||||
* @returns {*|number} |
||||
*/ |
||||
Array.prototype.minFor = function () { |
||||
return this.reduce((min, arr) => Math.min(min, arr.length), Infinity); |
||||
} |
||||
|
||||
/** |
||||
* 让每个子数组只保留前n个元素 |
||||
* @param n |
||||
* @returns {*[]} |
||||
*/ |
||||
Array.prototype.startCut = function (n) { |
||||
return this.map(arr => arr.slice(0, n)); |
||||
} |
@ -0,0 +1,53 @@ |
||||
/** |
||||
* 调用示例: |
||||
* |
||||
* var template1="我是{0},今年{1}了"; |
||||
* var template2="我是{name},今年{age}了"; |
||||
* var result1=template1.format("loogn",22); |
||||
* var result2=template1.format({name:"loogn",age:22}); |
||||
*/ |
||||
|
||||
String.prototype.format = function (args) { |
||||
if (arguments.length > 0) { |
||||
var result = this; |
||||
if (arguments.length == 1 && typeof (args) == "object") { |
||||
for (var key in args) { |
||||
var reg = new RegExp("({" + key + "})", "g"); |
||||
result = result.replace(reg, args[key]); |
||||
} |
||||
} else { |
||||
for (var i = 0; i < arguments.length; i++) { |
||||
if (arguments[i] == undefined) { |
||||
return ""; |
||||
} else { |
||||
var reg = new RegExp("({[" + i + "]})", "g"); |
||||
result = result.replace(reg, arguments[i]); |
||||
} |
||||
} |
||||
} |
||||
return result; |
||||
} else { |
||||
return this; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 取出所有a到b中间的字符串 |
||||
* |
||||
* @param a |
||||
* @param b |
||||
*/ |
||||
String.prototype.between = function (a, b) { |
||||
if (arguments.length > 0) { |
||||
var results = []; |
||||
var reg = new RegExp(`\\${a}([^\\${b}]+)\\${b}`, "g"); |
||||
var match; |
||||
while ((match = reg.exec(this)) !== null) { |
||||
results.push(match[1]); |
||||
} |
||||
return results; |
||||
} else { |
||||
return this.between("{", "}"); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,195 @@ |
||||
export const defaultColorConfig = { |
||||
learnLevel: '#33ccff', |
||||
property: '#33cc33', |
||||
hotKey: '#ffcc00', |
||||
level: '#33ccff', |
||||
learnUpdateLevel: '#ff9900', |
||||
nature: '#ffcccc' |
||||
} |
||||
|
||||
export function createObjs(len, fn) { |
||||
let arr = []; |
||||
for (let i = 0; i < len; i++) { |
||||
let res = fn(i) |
||||
arr.push(res) |
||||
} |
||||
return arr |
||||
} |
||||
|
||||
export function formula2arr(str, keys = 'abcdefghijklmnopqrstuvwxyz'.split('')) { |
||||
// 解析数量
|
||||
let formulas = str.split(';'); |
||||
const num = parseInt(formulas[0]) |
||||
// 解析参数
|
||||
formulas = formulas[1].split('/'); |
||||
// 构建返回
|
||||
const result = []; |
||||
// 解析公式
|
||||
formulas = formulas.map((f) => { |
||||
let formula = f.split("+") |
||||
formula = formula.map(e => parseInt(e)) |
||||
return { |
||||
base: formula[0], |
||||
plus: formula[1] |
||||
} |
||||
}) |
||||
// 生成对象
|
||||
for (let i = 0; i < num; i++) { |
||||
let obj = {} |
||||
for (let j = 0; j < formulas.length; j++) { |
||||
let key = keys[j] |
||||
obj[key] = formulas[j].base + formulas[j].plus * i |
||||
} |
||||
result.push(obj) |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
function toCfg(s, c) { |
||||
if (typeof s !== 'string') { |
||||
return c |
||||
} |
||||
let items = s.split(", ") |
||||
let cfg = c |
||||
if (items.length > 0) { |
||||
items.forEach((e) => { |
||||
let p = e.split(":") |
||||
cfg[p[0]] = p[1] |
||||
}) |
||||
} |
||||
return cfg |
||||
} |
||||
|
||||
/** |
||||
* 以[xxx]的形式分析数据,自定义配置使用{}在参数str内附带,没有指定自定义配置时会使用默认配置 |
||||
* 例:[a=2/3/4][b=16+8][lv=3]{ls:/, fs:,} |
||||
* warning: 自定义配置以“, ”为分隔符,空格不可省略 |
||||
* |
||||
* @param str |
||||
* @param cfg <ul>配置: |
||||
* <li>fs:公式数据的分隔符</li> |
||||
* <li>ls:数值列表的分隔符</li> |
||||
* <li>es:属性的分隔符</li> |
||||
* <li>lv:等级的属性名</li> |
||||
* </ul> |
||||
* @returns {*[]} |
||||
*/ |
||||
export function conversion(str, cfg = {fs: "+", ls: ",", es: "=", lv: "lv"}) { |
||||
// 加载自定义配置
|
||||
cfg = toCfg(str.between()[0], cfg) |
||||
// 过滤无效信息
|
||||
const pairs = checkValid((str).between("[", "]")) |
||||
// 判断必备条件,否则返回空数组
|
||||
if (str.indexOf("[" + cfg.lv + cfg.es) === -1 || isNaN(parseInt((str).between("[" + cfg.lv + cfg.es, "]")))) { |
||||
return [] |
||||
} |
||||
const lv = parseInt((str).between("[" + cfg.lv + cfg.es, "]")) |
||||
let items = {} |
||||
let itemKeys = [] |
||||
const result = [] |
||||
pairs.forEach(pair => { |
||||
let p = pair.split(cfg.es) |
||||
itemKeys.push(p[0]) |
||||
if (p[1].indexOf(cfg.fs) !== -1) { // 有+号,是公式
|
||||
let fps = p[1].split(cfg.fs) |
||||
let rules = {base: parseFloat(fps[0]), plus: parseFloat(fps[1])} |
||||
items[p[0]] = createObjs(lv, i => rules.base + rules.plus * i) |
||||
} else { // 没有+号,是数值list
|
||||
items[p[0]] = arrCut(p[1].split(cfg.ls).filter(e => !isNaN(parseFloat(e))).map(e => parseFloat(e)), lv) |
||||
} |
||||
}) |
||||
for (let i = 0; i < lv; i++) { |
||||
let obj = {} |
||||
itemKeys.forEach((k, n) => { |
||||
obj[k] = items[k][i] |
||||
}) |
||||
result.push(obj) |
||||
} |
||||
return result |
||||
} |
||||
|
||||
/** |
||||
* 检查输入的内容是否为a=b的形式,并返回合格的内容 |
||||
* a的通过规则:为a-z的其中一个 |
||||
* b可以是任意字符串 |
||||
* |
||||
* @param arr |
||||
* @returns {*[]} |
||||
*/ |
||||
export function checkValid(arr) { |
||||
const result = []; |
||||
for (let i = 0; i < arr.length; i++) { |
||||
const str = arr[i]; |
||||
const regex = /^[a-z]=[^\s]+$/; |
||||
if (regex.test(str)) { |
||||
result.push(str); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
export function copyToClipboard(text) { |
||||
const textarea = document.createElement('textarea'); // 创建一个文本域元素
|
||||
textarea.value = text; // 将传入的文本内容赋值给文本域
|
||||
textarea.setAttribute('readonly', ''); // 设置文本域只读
|
||||
textarea.style.position = 'absolute'; |
||||
textarea.style.left = '-9999px'; // 将文本域定位到屏幕外
|
||||
document.body.appendChild(textarea); // 将文本域添加到页面上
|
||||
textarea.select(); // 选中文本域中的内容
|
||||
document.execCommand('copy'); // 将选中的内容复制到系统剪切板
|
||||
document.body.removeChild(textarea); // 移除文本域
|
||||
} |
||||
|
||||
|
||||
/** |
||||
* 如果a的长度超过b就返回a的前b项 |
||||
* 如果小于则添加字符串"null"使a的长度到达b |
||||
* 如果相等则直接返回a |
||||
* |
||||
* @param arr |
||||
* @param targetLength |
||||
* @returns {*} |
||||
*/ |
||||
function arrCut(arr, targetLength) { |
||||
if (arr.length > targetLength) { |
||||
return arr.slice(0, targetLength); |
||||
} else if (arr.length < targetLength) { |
||||
let nulls = new Array(targetLength - arr.length).fill("null"); |
||||
return arr.concat(nulls); |
||||
} else { |
||||
return arr; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 中文标点符号换成英文标点符号,以赋予输入中文符号不出错的能力 |
||||
* |
||||
* @param str |
||||
* @returns {*} |
||||
* @constructor |
||||
*/ |
||||
export function symbolCn2En(str) { |
||||
if (Array.isArray(str)) { |
||||
return str.map(s => symbolCn2En(s)); |
||||
} |
||||
const map = { |
||||
',': ',', |
||||
'。': '.', |
||||
'?': '?', |
||||
'!': '!', |
||||
':': ':', |
||||
';': ';', |
||||
'“': '"', |
||||
'”': '"', |
||||
'‘': "'", |
||||
'’': "'", |
||||
'(': '(', |
||||
')': ')', |
||||
'【': '[', |
||||
'】': ']', |
||||
'《': '<', |
||||
'》': '>', |
||||
'、': ',' |
||||
}; |
||||
return str.replaceAll(/[\u4e00-\u9fa5]/g, c => map[c] || c); |
||||
} |
@ -0,0 +1,272 @@ |
||||
<template> |
||||
<div class="app-container"> |
||||
<el-form ref="form" :model="colorConfig" label-width="110px"> |
||||
<el-col :span="11" class="example-box"> |
||||
<spell |
||||
v-if="e.name === exampleSpell" |
||||
v-for="(e,i) in exampleSpellList" |
||||
:spell-data="e" |
||||
:color-config="colorConfig" |
||||
:module="effectExample" |
||||
/> |
||||
</el-col> |
||||
<el-col :span="12" :offset="1" class="example-setting"> |
||||
<el-row style="text-align:left;"> |
||||
<el-divider content-position="left">示例设置</el-divider> |
||||
<p> |
||||
<el-radio-group v-model="effectExample" size="mini"> |
||||
<el-radio-button label="学习"></el-radio-button> |
||||
<el-radio-button label="普通"></el-radio-button> |
||||
</el-radio-group> |
||||
</p> |
||||
<p> |
||||
<el-radio-group v-model="exampleSpell" size="mini" @change="handleExampleChange"> |
||||
<el-radio-button v-for="(e,i) in exampleSpellList" :label="e.name"></el-radio-button> |
||||
</el-radio-group> |
||||
</p> |
||||
<p> |
||||
<el-radio-group v-model="exampleLevel" size="mini" @change="handleExampleLevelChange"> |
||||
<el-radio-button |
||||
v-for="(e,i) in exampleLevelList" |
||||
:label="(i+1)" |
||||
>{{ (1 + i) + '级' }} |
||||
</el-radio-button> |
||||
</el-radio-group> |
||||
</p> |
||||
</el-row> |
||||
<el-row style="margin-top: 15px;"> |
||||
<el-form-item label="热键:" style="margin-bottom: 0;"> |
||||
<el-col :span="5"> |
||||
<el-color-picker size="mini" v-model="colorConfig.hotKey"/> |
||||
</el-col> |
||||
<el-col :span="7">{{ colorConfig.hotKey }}</el-col> |
||||
</el-form-item> |
||||
<el-form-item label="学习等级:" style="margin-bottom: 0;"> |
||||
<el-col :span="5"> |
||||
<el-color-picker size="mini" v-model="colorConfig.learnLevel"/> |
||||
</el-col> |
||||
<el-col :span="7">{{ colorConfig.learnLevel }}</el-col> |
||||
</el-form-item> |
||||
<el-form-item label="技能属性:" style="margin-bottom: 0;"> |
||||
<el-col :span="5"> |
||||
<el-color-picker size="mini" v-model="colorConfig.property"/> |
||||
</el-col> |
||||
<el-col :span="7">{{ colorConfig.property }}</el-col> |
||||
</el-form-item> |
||||
<el-form-item label="升级等级前缀:" style="margin-bottom: 0;"> |
||||
<el-col :span="5"> |
||||
<el-color-picker size="mini" v-model="colorConfig.learnUpdateLevel"/> |
||||
</el-col> |
||||
<el-col :span="7">{{ colorConfig.learnUpdateLevel }}</el-col> |
||||
</el-form-item> |
||||
<el-form-item label="特殊描述:" style="margin-bottom: 0;"> |
||||
<el-col :span="5"> |
||||
<el-color-picker size="mini" v-model="colorConfig.nature"/> |
||||
</el-col> |
||||
<el-col :span="7">{{ colorConfig.nature }}</el-col> |
||||
</el-form-item> |
||||
<div style="text-align:right;margin-top: 15px;margin-bottom: 15px;"> |
||||
<el-button size="mini" type="primary" plain @click="resetColorConfig">恢复默认</el-button> |
||||
<el-button size="mini" type="success" plain @click="saveColorConfig">保存</el-button> |
||||
<el-button size="mini" type="warning" plain @click="loadColorConfig">读取</el-button> |
||||
<el-button size="mini" type="danger" plain @click="removeColorConfig">删除</el-button> |
||||
</div> |
||||
</el-row> |
||||
</el-col> |
||||
</el-form> |
||||
|
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import Spell from "@/views/wc3word/spell"; |
||||
import {createObjs, defaultColorConfig} from "@/utils/tools"; |
||||
|
||||
|
||||
export default { |
||||
name: 'Color', |
||||
props: { |
||||
colorDefaultConfig: Object |
||||
}, |
||||
components: {Spell, defaultColorConfig}, |
||||
data() { |
||||
return { |
||||
colorCfg: {}, |
||||
exampleSpell: null, |
||||
exampleLevel: 1, |
||||
effectExample: '学习', |
||||
exampleSpellList: [ |
||||
{ |
||||
id: "Ahtb", |
||||
lv: "1", |
||||
name: "风暴之锤", |
||||
hotKey: "T", |
||||
effect: "向目标投掷一巨大的魔法锤,对其造成一定伤害并使其陷入眩晕。", |
||||
pros: [ |
||||
{name: '法力消耗', val: '75'}, |
||||
{name: '施法距离', val: '800'}, |
||||
{name: '冷却时间', val: '6'}, |
||||
], |
||||
updateWord: { |
||||
text: "{d}点伤害,{t}秒的晕眩时间", |
||||
vals: createObjs(5, i => ({d: 100 + i * 110, t: 3 + i})) |
||||
}, |
||||
nature: "", |
||||
normalWord: { |
||||
text: '向目标投掷一巨大的魔法锤,对其造成{d}点伤害并使其{t}秒内处于眩晕状态。', |
||||
vals: createObjs(5, i => ({d: 100 + i * 110, t: 3 + i})) |
||||
} |
||||
}, |
||||
{ |
||||
id: "Ahbz", |
||||
lv: "1", |
||||
name: "暴风雪", |
||||
hotKey: "F", |
||||
effect: "能召唤出若干次冰片攻击,对目标区域内的单位造成一定的伤害。", |
||||
pros: [ |
||||
{name: '法力消耗', val: '75'}, |
||||
{name: '施法距离', val: '600'}, |
||||
{name: '冷却时间', val: '9'}, |
||||
], |
||||
updateWord: { |
||||
text: "召唤{c}次,每次{d}点伤害。", |
||||
vals: createObjs(6, i => ({c: i * 2 + 6, d: i * 10 + 30})) |
||||
}, |
||||
nature: "需要持续施法", |
||||
normalWord: { |
||||
text: '召唤出{c}次的冰片攻击,每一次攻击能对一小块区域内的单位造成{d}的伤害值。', |
||||
vals: createObjs(6, i => ({c: i * 2 + 6, d: i * 10 + 30})) |
||||
} |
||||
}, |
||||
], |
||||
} |
||||
}, |
||||
computed: { |
||||
exampleLevelList() { |
||||
return this.exampleSpellList.filter(e => e.name === this.exampleSpell)[0].updateWord.vals.length |
||||
}, |
||||
colorConfig() { |
||||
return this.colorCfg |
||||
} |
||||
}, |
||||
watch: { |
||||
colorCfg() { |
||||
this.$emit("updateCfg", this.colorCfg) |
||||
} |
||||
}, |
||||
created() { |
||||
this.colorCfg = Object.assign({}, this.colorDefaultConfig) |
||||
this.exampleSpell = this.exampleSpellList[0].name |
||||
}, |
||||
mounted() { |
||||
}, |
||||
methods: { |
||||
saveColorConfig() { |
||||
// console.log('saving') |
||||
let colorConfigList = JSON.parse(localStorage.getItem('color-config-list') || '[]') |
||||
let colorCfgNames = colorConfigList.map(e => e.name) |
||||
this.$prompt('取个名字吧!\n会覆盖同名的方案。', '提示', { |
||||
confirmButtonText: '确定', |
||||
// cancelButtonText: '取消', |
||||
showCancelButton: false, |
||||
showClose: false, |
||||
}).then(({value}) => { |
||||
let index = colorCfgNames.indexOf(value) |
||||
if (index === -1) { |
||||
colorConfigList.push({name: value, cfg: this.colorConfig}) |
||||
} else { |
||||
colorConfigList[index].cfg = this.colorConfig |
||||
} |
||||
localStorage.setItem('color-config-list', JSON.stringify(colorConfigList)) |
||||
this.$message({type: 'success', message: '保存成功!'}); |
||||
}).catch(() => { |
||||
}); |
||||
}, |
||||
loadColorConfig() { |
||||
let colorConfigList = JSON.parse(localStorage.getItem('color-config-list') || '[]') |
||||
if (colorConfigList.length > 0) { |
||||
let colorCfgNames = colorConfigList.map(e => e.name) |
||||
this.$prompt('你要读取哪个方案?\n已保存方案:\n' + colorCfgNames, '提示', { |
||||
confirmButtonText: '确定', |
||||
// cancelButtonText: '取消', |
||||
showCancelButton: false, |
||||
showClose: false, |
||||
}).then(({value}) => { |
||||
let index = colorCfgNames.indexOf(value) |
||||
if (index === -1) { |
||||
this.$message({type: 'error', message: '方案不存在!'}); |
||||
} else { |
||||
this.colorConfig = colorConfigList[index].cfg |
||||
this.$message({type: 'success', message: '读取成功!'}); |
||||
} |
||||
}).catch(() => { |
||||
}); |
||||
} else { |
||||
this.$message({type: 'error', message: '没有已保存的方案!'}); |
||||
} |
||||
}, |
||||
removeColorConfig() { |
||||
let colorConfigList = JSON.parse(localStorage.getItem('color-config-list') || '[]') |
||||
if (colorConfigList.length > 0) { |
||||
let colorCfgNames = colorConfigList.map(e => e.name) |
||||
this.$prompt('你要删除哪个方案?\n已保存方案的名称有:\n' + colorCfgNames, |
||||
'提示', { |
||||
confirmButtonText: '确定', |
||||
// cancelButtonText: '取消', |
||||
showCancelButton: false, |
||||
showClose: false, |
||||
}).then(({value}) => { |
||||
let index = colorCfgNames.indexOf(value) |
||||
if (index === -1) { |
||||
this.$message({type: 'error', message: '方案不存在!'}); |
||||
} else { |
||||
colorConfigList = colorConfigList.filter(e => e !== colorConfigList[index]) |
||||
localStorage.setItem('color-config-list', JSON.stringify(colorConfigList)) |
||||
this.$message({type: 'success', message: '操作成功!'}); |
||||
} |
||||
}).catch(() => { |
||||
}); |
||||
} else { |
||||
this.$message({type: 'error', message: '没有已保存的方案!'}); |
||||
} |
||||
}, |
||||
resetColorConfig() { |
||||
// console.log('reset') |
||||
this.colorCfg = Object.assign({}, defaultColorConfig) |
||||
}, |
||||
handleExampleChange() { |
||||
this.exampleSpellList.map(e => e.lv = 1) |
||||
this.exampleLevel = 1 |
||||
}, |
||||
handleExampleLevelChange() { |
||||
this.exampleSpellList.map(e => e.lv = this.exampleLevel) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
|
||||
.example-setting el-form-item { |
||||
el-color-picker { |
||||
min-width: 80px; |
||||
} |
||||
|
||||
margin-bottom: 0; |
||||
} |
||||
|
||||
.example-setting p { |
||||
margin-top: 15px; |
||||
} |
||||
|
||||
.example-box { |
||||
max-height: 40vh; |
||||
} |
||||
|
||||
.example-box p { |
||||
font-size: 12px !important; |
||||
font-weight: bold !important; |
||||
overflow-y: auto; |
||||
max-height: 30vh; |
||||
} |
||||
</style> |
@ -0,0 +1,184 @@ |
||||
<template> |
||||
<el-dialog ref="dialog" width="50%" :title="title" :visible.sync="visible" @close="closeDialog" |
||||
:append-to-body="true" :show-close="false" :transition="dialogTransition"> |
||||
<div class="builder-dialog"> |
||||
<el-col :span="12" style="padding-right: 20px;"> |
||||
<el-divider content-position="center">结果展示</el-divider> |
||||
<spell :spell-data="spell" :module="spellModule" :color-config="color"/> |
||||
</el-col> |
||||
<el-col :span="11" :offset="1" class="cp-rect"> |
||||
<el-divider content-position="left">结果展示设置</el-divider> |
||||
<el-radio-group v-model="spellModule" size="mini"> |
||||
<el-radio-button label="学习"></el-radio-button> |
||||
<el-radio-button label="普通"></el-radio-button> |
||||
</el-radio-group> |
||||
<el-row class="cp-item"> |
||||
<el-radio-group v-model="spellData.lv" size="mini"> |
||||
<el-radio-button |
||||
v-for="(e,i) in exampleLevelList" |
||||
:label="(i+1)" |
||||
>{{ (1 + i) + '级' }} |
||||
</el-radio-button> |
||||
</el-radio-group> |
||||
</el-row> |
||||
<el-row class="cp-item"> |
||||
<el-divider content-position="left">点击复制</el-divider> |
||||
<el-button-group> |
||||
<el-button type="success" plain size="mini" |
||||
@click="handleBuilder('title')">学习标题 |
||||
</el-button> |
||||
<el-button type="success" plain size="mini" |
||||
@click="handleBuilder('export')">学习扩展 |
||||
</el-button> |
||||
</el-button-group> |
||||
</el-row> |
||||
<el-row class="cp-item" v-for="(e,i) in spell.updateWord.vals"> |
||||
<el-button-group> |
||||
<el-button :type="i%2===1?'success':'primary'" plain size="mini" |
||||
@click="handleBuilder('title', (i+1))"> |
||||
{{ (i + 1) }}级标题 |
||||
</el-button> |
||||
<el-button :type="i%2===1?'success':'primary'" plain size="mini" |
||||
@click="handleBuilder('export', (i+1))"> |
||||
{{ (i + 1) }}级扩展 |
||||
</el-button> |
||||
</el-button-group> |
||||
</el-row> |
||||
</el-col> |
||||
</div> |
||||
<div slot="footer" class="dialog-footer"> |
||||
<el-button @click="visible = false" size="mini">关闭</el-button> |
||||
</div> |
||||
</el-dialog> |
||||
</template> |
||||
|
||||
<script> |
||||
import Spell from "@/views/wc3word/spell"; |
||||
import {copyToClipboard} from "@/utils/tools"; |
||||
|
||||
export default { |
||||
name: 'SpellBuilderDialog', |
||||
props: { |
||||
spellData: { |
||||
type: Object, |
||||
default: () => false |
||||
}, |
||||
colorConfig: Object, |
||||
show: { |
||||
type: Boolean, |
||||
default: () => false |
||||
} |
||||
}, |
||||
components: {Spell}, |
||||
data() { |
||||
return { |
||||
visible: this.show, |
||||
spellModule: '学习', |
||||
dialogTransition: 'dialog-fade' |
||||
} |
||||
}, |
||||
computed: { |
||||
spell() { |
||||
return this.spellData |
||||
}, |
||||
exampleLevelList() { |
||||
return this.spellData.updateWord.vals.length || 1 |
||||
}, |
||||
color() { |
||||
return this.colorConfig |
||||
}, |
||||
title() { |
||||
return '技能文本生成结果' |
||||
} |
||||
}, |
||||
watch: { |
||||
visible() { |
||||
this.$emit('update:show', false) |
||||
} |
||||
}, |
||||
created() { |
||||
|
||||
}, |
||||
mounted() { |
||||
}, |
||||
methods: { |
||||
closeDialog() { |
||||
// 关闭 |
||||
}, |
||||
handleSave() { |
||||
}, |
||||
dyeing(str, color) { |
||||
const prefix = '|cfff' |
||||
const suffix = '|r' |
||||
return color ? (prefix + color.replace('#', "") + str + suffix) : str |
||||
}, |
||||
handleBuilder(module, lv) { |
||||
const {learnLevel, property, hotKey, level, learnUpdateLevel, nature} = this.color |
||||
const sp = this.spell |
||||
let result = '' |
||||
if (!lv) { |
||||
// 无lv 是学习文本 |
||||
lv = '%d' |
||||
if (module === 'title') { |
||||
result = `学习 ${this.dyeing(lv + ' 级', learnLevel)} ${sp.name}(${this.dyeing(sp.hotKey, hotKey)})` |
||||
} else if (module === 'export') { |
||||
result += sp.effect + '\n' |
||||
result += sp.natual ? (sp.natual + '\n') : '' |
||||
sp.pros.forEach(e => { |
||||
result += `\n${this.dyeing(e.name, property)}:${e.val}` |
||||
}) |
||||
if (sp.updateWord.text && sp.updateWord.vals) { |
||||
result += "\n" |
||||
sp.updateWord.vals.forEach((v, i) => { |
||||
let w = sp.updateWord.text.format(v) |
||||
w = w.endsWith("。") ? w : w + "。" |
||||
result += `\n${this.dyeing((i + 1) + '级', learnUpdateLevel)} - ${w}` |
||||
}) |
||||
} |
||||
} |
||||
} else { |
||||
// 有lv 是普通文本 |
||||
if (module === 'title') { |
||||
result = `${sp.name}(${this.dyeing(sp.hotKey, hotKey)}) - [${this.dyeing(lv + ' 级', learnLevel)}]` |
||||
} else if (module === 'export') { |
||||
let n = sp.normalWord |
||||
let ps = sp.pros.filter(e => e.name !== '法力消耗') |
||||
result += n.text.format(n.vals[lv - 1]) |
||||
result += sp.natual ? (sp.natual + '\n') : '' |
||||
if (ps && ps.length > 0) { |
||||
result += '\n' |
||||
ps.forEach(p => { |
||||
result += `\n${this.dyeing(p.name, property)}:${p.val}` |
||||
}) |
||||
} |
||||
} |
||||
} |
||||
// console.log('result -> ', result) |
||||
// return result |
||||
copyToClipboard(result) |
||||
const h = this.$createElement; |
||||
this.$notify({ |
||||
title: '来自Soul2的提示', |
||||
message: h('span', {style: 'color: teal'}, '生成的文本已输出到剪切板。'), |
||||
position: 'bottom-right', |
||||
duration: 3000, |
||||
// showClose: false |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.builder-dialog { |
||||
overflow-y: auto; |
||||
min-height: 35vh; |
||||
padding-left: 5%; |
||||
padding-right: 5%; |
||||
|
||||
.cp-rect .cp-item { |
||||
margin-top: 8px; |
||||
margin-bottom: 8px; |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,51 @@ |
||||
<template> |
||||
<el-dialog ref="dialogForm" width="45%" :title="title" :visible.sync="visible" @close="closeDialog" |
||||
:append-to-body="true" :show-close="false"> |
||||
<!-- <div slot="footer" class="dialog-footer">--> |
||||
<!-- <el-button @click="visible = false">取消</el-button>--> |
||||
<!-- <el-button type="primary" @click="handleSave">保存</el-button>--> |
||||
<!-- </div>--> |
||||
</el-dialog> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'UpdateRecordDialog', |
||||
props: { |
||||
show: { |
||||
type: Boolean, |
||||
default: () => false |
||||
} |
||||
}, |
||||
components: {}, |
||||
data() { |
||||
return { |
||||
visible: this.show, |
||||
} |
||||
}, |
||||
computed: { |
||||
title() { |
||||
return '更新记录' |
||||
} |
||||
}, |
||||
watch: { |
||||
visible() { |
||||
this.$emit('update:show', false) |
||||
} |
||||
}, |
||||
created() { |
||||
|
||||
}, |
||||
mounted() { |
||||
}, |
||||
methods: { |
||||
closeDialog() { |
||||
// 关闭 |
||||
}, |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
|
||||
</style> |
@ -0,0 +1,286 @@ |
||||
<template> |
||||
<div class="edit-container"> |
||||
<el-col :span="9" style="padding-top: 2vh;text-align:left;"> |
||||
<el-row class="spell-info-setting"> |
||||
<el-radio-group v-model="spellInfo.module" size="mini"> |
||||
<el-radio-button label="学习"></el-radio-button> |
||||
<el-radio-button label="普通"></el-radio-button> |
||||
</el-radio-group> |
||||
</el-row> |
||||
<el-row class="spell-info-setting"> |
||||
<el-radio-group v-model="spellData.lv" size="mini"> |
||||
<el-radio-button |
||||
v-for="(e,i) in exampleLevelList" |
||||
:label="(i+1)" |
||||
>{{ (1 + i) + '级' }} |
||||
</el-radio-button> |
||||
</el-radio-group> |
||||
</el-row> |
||||
<el-row class="spell-info-setting"> |
||||
<el-button size="mini" type="success" plain @click="handleBuilder">生成</el-button> |
||||
<el-button size="mini" type="primary" plain @click="handleResetSpellData">恢复默认</el-button> |
||||
</el-row> |
||||
<spell :module="spellInfo.module" :spell-data="spellData" :color-config="colorConfig"/> |
||||
</el-col> |
||||
<el-col :span="14" :offset="1"> |
||||
<el-card shadow="always"> |
||||
<el-form class="edit-form" size="mini" ref="editForm" v-model="spellData" label-width="90px"> |
||||
<el-divider content-position="left">在此填写</el-divider> |
||||
<el-row> |
||||
<el-col :span="18"> |
||||
<el-form-item label="名称"> |
||||
<el-col :span="21"> |
||||
<el-input v-model="spellData.name" placeholder="技能名称"/> |
||||
</el-col> |
||||
</el-form-item> |
||||
</el-col> |
||||
<el-col :span="6"> |
||||
<el-form-item label="热键" label-width="45px"> |
||||
<el-input v-model="spellData.hotKey" placeholder="热键"/> |
||||
</el-form-item> |
||||
</el-col> |
||||
</el-row> |
||||
<el-row> |
||||
<el-form-item label="技能描述"> |
||||
<el-input type="textarea" :autosize="{ minRows: 2, maxRows: 3}" |
||||
placeholder="例:能召唤出若干次冰片攻击,对目标区域内的单位造成一定的伤害。" |
||||
v-model="spellData.effect"/> |
||||
</el-form-item> |
||||
</el-row> |
||||
<el-row> |
||||
<el-form-item label="特点"> |
||||
<el-input type="textarea" :autosize="{ minRows: 2, maxRows: 3}" placeholder="例:需要持续施法" |
||||
v-model="spellData.nature"/> |
||||
</el-form-item> |
||||
</el-row> |
||||
<el-row> |
||||
<el-form-item> |
||||
<template slot="label"> |
||||
<span>升级描述</span> |
||||
<el-tooltip placement="top" effect="light"> |
||||
<template slot="content"> |
||||
用{}来标记数值的位置,{}内用<span style="color:#f00;">各不相同</span>的小写abcd来区分。<br> |
||||
例:{a}点伤害,晕眩时间{b}秒。<br> |
||||
数值的语法:<br> |
||||
[lv=3][a=1,2,3][b=20+10]<br> |
||||
其中a、b与上述数值的标记对应;lv=3表示一共有3个等级 <br> |
||||
[b=20+10]中 20+10表示公式,意思是1级是20,后面每级+10;而[a=1,2,3]表示每个等级的数值 |
||||
</template> |
||||
<i class="el-icon-question" style="cursor:pointer;"></i> |
||||
</el-tooltip> |
||||
</template> |
||||
<el-input placeholder="学习技能时显示的“1级 - xx伤害,xx秒时间。”" |
||||
v-model="spellData.updateWord.text" @change="handleTempDataForUpdate"/> |
||||
<el-input type="textarea" :autosize="{ minRows: 2, maxRows: 4}" |
||||
placeholder="数值" |
||||
v-model="tempData.updateWord" @change="handleTempDataForUpdate"/> |
||||
</el-form-item> |
||||
</el-row> |
||||
<el-row> |
||||
<el-form-item> |
||||
<template slot="label"> |
||||
<span>普通描述</span> |
||||
<el-tooltip placement="top" effect="light"> |
||||
<template slot="content"> |
||||
用{}来标记数值的位置,{}内用<span style="color:#f00;">各不相同</span>的小写abcd来区分。<br> |
||||
例:造成{a}点伤害并使其{b}秒内陷入晕眩。 |
||||
数值的语法:<br> |
||||
[lv=3][a=1,2,3][b=20+10]<br> |
||||
其中a、b与上述数值的标记对应;lv=3表示一共有3个等级 <br> |
||||
[b=20+10]中 20+10表示公式,意思是1级是20,后面每级+10;而[a=1,2,3]表示每个等级的数值 |
||||
</template> |
||||
<i class="el-icon-question" style="cursor:pointer;"></i> |
||||
</el-tooltip> |
||||
</template> |
||||
<el-input type="textarea" :autosize="{ minRows: 2, maxRows: 4}" |
||||
placeholder="向目标投掷一巨大的魔法锤,对其造成100点伤害并使其3秒内处于眩晕状态。" |
||||
v-model="spellData.normalWord.text" @change="handleTempDataForNormal"/> |
||||
<el-input type="textarea" :autosize="{ minRows: 2, maxRows: 4}" |
||||
placeholder="数值" |
||||
v-model="tempData.normalWord" @change="handleTempDataForNormal"/> |
||||
</el-form-item> |
||||
</el-row> |
||||
<el-divider content-position="left">技能属性</el-divider> |
||||
<el-row v-for="(e,i) in spellData.pros" style="margin-bottom: 12px;"> |
||||
<el-col :span="2" style="line-height: 40px;text-align:right;"> |
||||
<i class="el-icon-remove" style="font-size: 18px; margin-right: 5px;cursor: pointer;color:#F56C6C;" |
||||
@click="spellData.pros = spellData.pros.filter(f=>f!==e)"></i> |
||||
</el-col> |
||||
<el-col :span="13"> |
||||
<el-autocomplete class="inline-input" v-model="e.name" placeholder="请输入名称" |
||||
:fetch-suggestions="querySearch"></el-autocomplete> |
||||
</el-col> |
||||
<el-col :span="9"> |
||||
<el-input v-model="e.val" placeholder="请输入值"/> |
||||
</el-col> |
||||
</el-row> |
||||
<div style="text-align:left;padding-left: 50px;margin-bottom: 20px;"> |
||||
<el-link type="primary" @click="addSpellPro">添加...</el-link> |
||||
</div> |
||||
</el-form> |
||||
</el-card> |
||||
</el-col> |
||||
<spell-builder-dialog v-if="spellBuilderCfg.show" :show.sync="spellBuilderCfg.show" |
||||
:spell-data="spellBuilderCfg.data" :color-config="colorConfig"/> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import Spell from "@/views/wc3word/spell"; |
||||
import {conversion} from "@/utils/tools"; |
||||
import SpellBuilderDialog from "@/views/wc3word/dialog/SpellBuilder"; |
||||
|
||||
const default_spell_data = { |
||||
id: "A000", |
||||
lv: "1", |
||||
name: null, |
||||
hotKey: null, |
||||
effect: null, |
||||
pros: [{name: '法力消耗', val: null},], |
||||
updateWord: {text: '', vals: []}, |
||||
nature: undefined, |
||||
normalWord: {text: '', vals: []}, |
||||
} |
||||
const default_spell_info = { |
||||
module: '学习', |
||||
} |
||||
const default_spell_pro_list = [ |
||||
{name: '法力消耗', val: null}, |
||||
{name: '施法距离', val: null}, |
||||
{name: '冷却时间', val: null}, |
||||
{name: '持续时间', val: null}, |
||||
{name: '法术范围', val: null}, |
||||
{name: '吟唱时间', val: null}, |
||||
] |
||||
|
||||
export default { |
||||
name: 'Edit', |
||||
props: { |
||||
colorConfig: Object |
||||
}, |
||||
components: {SpellBuilderDialog, Spell}, |
||||
data() { |
||||
return { |
||||
tempData: {}, |
||||
spellBuilderCfg: { |
||||
show: false, |
||||
data: null |
||||
}, |
||||
spellDataObj: Object.assign({}, default_spell_data), |
||||
spellInfo: Object.assign({}, default_spell_info), |
||||
} |
||||
}, |
||||
computed: { |
||||
spellData() { |
||||
return this.spellDataObj |
||||
}, |
||||
spellDefaultProNames() { |
||||
return default_spell_pro_list.map(e => ({value: e.name})) |
||||
}, |
||||
exampleLevelList() { |
||||
return this.spellData.updateWord.vals.length || 1 |
||||
}, |
||||
}, |
||||
watch: { |
||||
spellDataObj: { |
||||
handler(newVal) { |
||||
localStorage.setItem('lastSpellData', JSON.stringify(newVal)); |
||||
}, |
||||
deep: true // 监听嵌套属性的变化 |
||||
}, |
||||
tempData: { |
||||
handler(newVal) { |
||||
localStorage.setItem('lastTempData', JSON.stringify(newVal)); |
||||
}, |
||||
deep: true // 监听嵌套属性的变化 |
||||
}, |
||||
}, |
||||
created() { |
||||
if (localStorage.getItem('lastSpellData') != null) { |
||||
// console.log(localStorage.getItem('lastSpellData')) |
||||
this.spellDataObj = JSON.parse(localStorage.getItem('lastSpellData')) |
||||
} |
||||
if (localStorage.getItem('lastTempData') != null) { |
||||
// console.log(localStorage.getItem('lastTempData')) |
||||
this.tempData = JSON.parse(localStorage.getItem('lastTempData')) |
||||
} |
||||
}, |
||||
mounted() { |
||||
}, |
||||
methods: { |
||||
querySearch(queryString, cb) { |
||||
var restaurants = this.spellDefaultProNames; |
||||
var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants; |
||||
// 调用 callback 返回建议列表的数据 |
||||
cb(results); |
||||
}, |
||||
createFilter(queryString) { |
||||
return (restaurant) => { |
||||
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0); |
||||
}; |
||||
}, |
||||
addSpellPro() { |
||||
this.spellData.pros.push({}) |
||||
}, |
||||
handleResolveTempData(s) { |
||||
var sStrList = s.between("[", "]") |
||||
var result = [] |
||||
sStrList.forEach((sStr, i) => { |
||||
var vs = sStr.split(",") |
||||
result.push(vs) |
||||
}) |
||||
var minLv = result.minFor() |
||||
if (!minLv) { |
||||
return minLv |
||||
} |
||||
return result.startCut(minLv) |
||||
}, |
||||
handleTempDataForNormal() { |
||||
var ori = this.tempData.normalWord |
||||
var ns = this.spellData.normalWord |
||||
if (ori && ns.text) { |
||||
ns.vals = conversion(ori) |
||||
} |
||||
}, |
||||
handleTempDataForUpdate() { |
||||
var ori = this.tempData.updateWord |
||||
var ns = this.spellData.updateWord |
||||
if (ori && ns.text) { |
||||
ns.vals = conversion(ori) |
||||
} |
||||
}, |
||||
handleBuilder() { |
||||
// console.log('开始生成 -> ', this.spellData) |
||||
this.spellBuilderCfg.show = true |
||||
this.spellBuilderCfg.data = Object.assign({}, this.spellData) |
||||
}, |
||||
handleResetSpellData() { |
||||
this.spellDataObj = Object.assign({}, default_spell_data) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.edit-container { |
||||
* { |
||||
font-weight: bold; |
||||
font-size: 12px; |
||||
} |
||||
|
||||
.spell-info-setting { |
||||
margin-bottom: 15px; |
||||
} |
||||
|
||||
.edit-form { |
||||
padding-right: 5%; |
||||
padding-left: 5%; |
||||
overflow-y: auto; |
||||
max-height: 68vh; |
||||
} |
||||
|
||||
padding-left: 5%; |
||||
padding-right: 5%; |
||||
max-height: 68vh; |
||||
} |
||||
</style> |
@ -0,0 +1,63 @@ |
||||
<template> |
||||
<div class="app-container"> |
||||
<el-tabs type="border-card" style="min-height: 50vh;"> |
||||
<!-- <el-tab-pane>--> |
||||
<!-- <span slot="label">简介</span>--> |
||||
<!-- <div>这是一个wc3编辑器文本生成器,它还在制作中。</div>--> |
||||
<!-- </el-tab-pane>--> |
||||
<el-tab-pane> |
||||
<span slot="label"><i class="el-icon-edit"></i>编辑文本</span> |
||||
<edit :color-config="colorConfig"/> |
||||
</el-tab-pane> |
||||
<el-tab-pane> |
||||
<span slot="label"><i class="el-icon-setting"></i>色彩</span> |
||||
<color :color-default-config="colorConfig" style="padding-left: 3%;padding-right: 3%;" |
||||
@updateCfg="updateCfg"/> |
||||
</el-tab-pane> |
||||
</el-tabs> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import Color from "@/views/wc3word/color"; |
||||
import Edit from "@/views/wc3word/edit"; |
||||
import {defaultColorConfig} from '@/utils/tools' |
||||
|
||||
|
||||
export default { |
||||
name: 'Word', |
||||
components: {Edit, Color}, |
||||
data() { |
||||
return { |
||||
colorConfig: Object.assign({}, defaultColorConfig), |
||||
version: '1.0.3' |
||||
} |
||||
}, |
||||
computed: {}, |
||||
watch: {}, |
||||
created() { |
||||
}, |
||||
mounted() { |
||||
const h = this.$createElement; |
||||
this.$notify({ |
||||
title: '来自Soul2的提示', |
||||
message: h('span', {style: 'color: teal'}, `当前正在使用的生成器版本是${this.version}。`), |
||||
position: 'bottom-right', |
||||
duration: 4000, |
||||
// showClose: false |
||||
}); |
||||
}, |
||||
methods: { |
||||
updateCfg(cfg) { |
||||
this.colorConfig = cfg |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.app-container { |
||||
padding-left: 15%; |
||||
padding-right: 15%; |
||||
} |
||||
</style> |
@ -0,0 +1,127 @@ |
||||
<template> |
||||
<div class="spell-container" :style="{fontSize: (fontSize || 12) + 'px'}"> |
||||
<div v-if="module === '学习'"> |
||||
<p style="margin-bottom: 3px;"> |
||||
学习 <span :style="{color: colorConfig.learnLevel}">{{ spellData.lv || '%d' }}级</span> |
||||
{{ spellData.name || '技能名称' }} |
||||
<span v-if="spellData.hotKey"> |
||||
(<span :style="{color: colorConfig.hotKey}">{{ spellData.hotKey }}</span>) |
||||
</span> |
||||
</p> |
||||
<hr> |
||||
<p style="margin-top: 7px;"> |
||||
{{ spellData.effect }} |
||||
<br/> |
||||
<span v-if="spellData.nature"> |
||||
<br><span :style="{color: colorConfig.nature}">{{ spellData.nature }}</span><br> |
||||
</span> |
||||
<br/> |
||||
<span v-if="p.val" v-for="(p,i) in learnPros" :key="i"> |
||||
<span :style="{color: colorConfig.property}"> |
||||
{{ p.name }}: |
||||
</span> |
||||
{{ p.key ? `<${spellData.id},${p.key}${p.lv}${p.r ? ',%' : ''}>` : (p.val || '(null)') }} |
||||
<br> |
||||
</span> |
||||
<span v-if="spellData.updateWord && spellData.updateWord.vals" |
||||
v-for="(l,i) in spellData.updateWord.vals"> |
||||
<br> |
||||
<span :style="{color: colorConfig.learnUpdateLevel}">{{ (i + 1) }}级</span> - |
||||
{{ spellData.updateWord.text.format(l) }} |
||||
{{ spellData.updateWord.text.endsWith("。") ? null : '。' }} |
||||
</span> |
||||
</p> |
||||
</div> |
||||
<div v-if="module === '普通'"> |
||||
<p style="margin-bottom: 3px;"> |
||||
{{ spellData.name || '技能名称' }} |
||||
<span v-if="spellData.hotKey">(<span :style="{color: colorConfig.hotKey}">{{ spellData.hotKey }}</span>)</span> |
||||
- |
||||
[<span :style="{color: colorConfig.learnLevel}">{{ spellData.lv || '1' }}级</span>] |
||||
</p> |
||||
<hr> |
||||
<p style="margin-top: 7px;"> |
||||
{{ spellData.normalWord.text.format(spellData.normalWord.vals[spellData.lv - 1]) }} |
||||
{{ spellData.normalWord.text.length === 0 || spellData.normalWord.text.endsWith("。") ? null : '。' }} |
||||
<br/> |
||||
<span v-if="spellData.nature"> |
||||
<br><span :style="{color: colorConfig.nature}">{{ spellData.nature }}</span><br> |
||||
</span> |
||||
<br v-if="normalPros.length > 0"> |
||||
<span v-if="p.val" v-for="(p,i) in normalPros" :key="i"> |
||||
<span :style="{color: colorConfig.property}"> |
||||
{{ p.name }}: |
||||
</span> |
||||
{{ p.key ? `<${spellData.id},${p.key}${p.lv}${p.r ? ',%' : ''}>` : (p.val || '(null)') }} |
||||
<br> |
||||
</span> |
||||
</p> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
|
||||
const default_spell_data = { |
||||
id: "A000", |
||||
lv: "1", |
||||
name: "名称丢失", |
||||
hotKey: "Q", |
||||
effect: "法术效果丢失", |
||||
pros: [], |
||||
updateWord: undefined, |
||||
nature: undefined, |
||||
normalWord: undefined, |
||||
} |
||||
|
||||
export default { |
||||
name: 'Spell', |
||||
props: { |
||||
module: { |
||||
default: () => '学习', |
||||
type: String |
||||
}, |
||||
spellData: { |
||||
default: () => Object.assign({}, default_spell_data), |
||||
type: Object |
||||
}, |
||||
colorConfig: { |
||||
default: () => { |
||||
}, |
||||
type: Object |
||||
}, |
||||
fontSize: Number |
||||
}, |
||||
components: {}, |
||||
data() { |
||||
return {} |
||||
}, |
||||
computed: { |
||||
normalPros() { |
||||
return this.spellData.pros.filter(e => e.name && e.name !== '法力消耗') |
||||
}, |
||||
learnPros() { |
||||
return this.spellData.pros.filter(e => e.name) |
||||
} |
||||
}, |
||||
watch: {}, |
||||
created() { |
||||
|
||||
}, |
||||
mounted() { |
||||
}, |
||||
methods: {} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.spell-container { |
||||
text-align: left; |
||||
font-weight: bold !important; |
||||
background-color: rgba(0, 0, 0, .75); |
||||
color: #ffffff; |
||||
border-radius: 5px; |
||||
padding: 3%; |
||||
box-sizing: border-box; |
||||
} |
||||
</style> |
Loading…
Reference in new issue