Commit 676ccf94 authored by joenix's avatar joenix

feat: update

parents
Pipeline #12939 canceled with stages
.DS_Store
node_modules
/dist
*.lock
*.log
# package-lock.json
# yarn.lock
/tests/e2e/videos/
/tests/e2e/screenshots/
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
module.exports = {
trailingComma: 'all',
tabWidth: 2,
semi: true,
singleQuote: true,
printWidth: 200,
};
export { default as plugin } from '@skylla/dev';
{
"name": "@skylla/materials-langzi",
"version": "0.3.13",
"description": "materials used in skylla",
"main": "index.js",
"author": "joenix",
"license": "MIT",
"private": false,
"scripts": {
"zip": "node-zip --from=./dist --to=./deploy/skylla-materials.zip",
"serve": "skylla-server",
"build": "skylla-render",
"dev": "skylla-dev",
"build:fat": "yarn build && yarn zip",
"build:prod": "yarn build && yarn zip"
},
"dependencies": {
"@skylla/dev": "^2.0.50",
"@skylla/sdk": "^2.0.9",
"ant-design-vue": "^2.2.8",
"jsbarcode": "^3.11.5",
"lodash": "^4.17.21",
"qrcode": "^1.4.4",
"vue-color-kit": "^1.0.5",
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"node-command-zip": "^1.4.7",
"process-env-argv": "^1.0.5"
}
}
# 开发说明
## 使用 `@skylla/dev` 搭建 物料库开发环境
```sh
npm i -d @skylla/dev
# or
yarn add -D @skylla/dev
```
```js
// package.json
{
"scripts": {
"serve": "server",
"build": "render",
}
}
```
```sh
# 1. 启动物料库本地浏览服务,默认端口:3000
yarn serve
# 2. 启动物料库开发监听服务
yarn build --watch
```
## 开启调试模式
```sh
https://printer.domain/visual?debug={material-namepsace}
```
## 目录结构
```
.
├─ index.js - 入口文件
├─ vue.config.js - 环境配置文件(参考 vue-cli 文档)
├─ /dist -(实时)编译输出目录
├─ /deploy - CICD 构建目录
└─ /src - 源码目录
└─ <material-name> // 组件目录
└─ index.vue // 组件主文件
└─ option.vue // 组件配置文件
```
## 数据结构
```js
{
/* 组件名称 */
"name": <string>,
/* 源信息 */
"meta": <object>,
/* 坐标信息 */
"axes": <object>,
/* 数源 - 参考:https://yapi.lcops.xyz/mock/913/getResource */
"source": <object>,
/* 配置 - 参考:各组件目录下 readme.md 文件 */
"option": {
/* 样式类 */
"css": <object>,
/* 预设类 */
"preset": <object>,
/* 表单类 */
"formic": <object>,
/* 存续类 - 于 非编辑时态 使用 */
"vars": <object>
},
};
```
## 组件结构
> index.vue
```js
export default {
// 组件注册名
name: "m-text",
// 组件源信息
meta: {
// 唯一身份标识
id: "952713",
// 图标(取自 Antv)
icon: "FormOutlined",
// 组件名称
label: "文本",
// 排序权重
index: 100,
// 可用性
disabled: false
},
// 坐标信息
axes: {
x: 0,
y: 0,
w: 200,
h: 160
},
// 入参: 整个 props 可以不写, 环境中默认有 mixin 处理
props: {
// 数源
source: {
type: [Object],
default() {
return {};
}
},
// 配置
option: {
type: [Object],
default() {
return {};
}
}
}
};
```
> option.vue
```js
// 纯净如新 (无需任何入参, 也可接受index.vue的prop)
export default {};
```
## 内置能力
1. STORE 数仓
```js
// 快速更新状态
// 注: `visual` 是 可视化平台 默认 命名空间
this.$store.update("visual", {
namespace: "printer"
});
// 内置状态
this.preview; // 打印状态识别
this.preset; // 属性预设
this.setting; // 全局设置
this.background; // 背景设置
this.one; // 当前栅格单元
this.axes; // 坐标信息
this.materials; // 组件(物料)信息
```
2. HTTP 请求
```js
// Send a Get Request
await this.$http(<address>).get(<params>, <option>);
// Send a Post Request
await this.$http(<address>).post(<params>, <option>);
```
3. 更多说明详见:[vue-scaff 开发指南](https://linkjs-pc-fat.lctest.cn/docsite/scaff/development.html)
## 版本发布
- 物料库发布时,将读取 `package.json` 中的 `version` 信息作为版本号
- 如需构建 `latest` 版本的,需在 `cicd` 构建过程中,添加参数 `latest` 即可
<template>
<div class="classifyNav" :style="option.css">
<div
v-if="option.data.length > 0"
class="classifyNav-List">
<div
class="classifyNav-List-item"
:style="doubleRowstyle"
v-for="(item, index) in option.data"
:key="index">
<navigator class="flexLayout" :url="`/pages/goodsList/main?classtreeCode=${item.classtreeCode}`" v-if="option.rowSwitch == 'single' ? index < 4: index < 8 ">
<div class="classifyNav-List-item-img" :style="imageStyle">
<img class="classifyNav-List-item-img-item" v-if="item.goodsClassLogo" :src="item.goodsClassLogo ? item.goodsClassLogo : '' " :onerror="defaultImg" />
</div>
<div class="classifyNav-List-item-text" v-if="item.goodsClassName">{{ item.goodsClassName }}</div>
</navigator>
</div>
</div>
<div class="classifyNav-List" v-else>
<div
class="classifyNav-List-item"
:style="doubleRowstyle"
v-for="(item, index) in option.default"
:key="index">
<div class="classifyNav-List-item-img" :style="imageStyle">
<img class="classifyNav-List-item-img-item" :src="item.goodsClassLogo" />
</div>
<div class="classifyNav-List-item-text">{{ item.goodsClassName }}</div>
</div>
<div
v-if="option.rowSwitch == 'double'"
class="classifyNav-List-item"
v-for="(item, index) in option.default"
:key="index" >
<div class="classifyNav-List-item-img" :style="imageStyle">
<img class="classifyNav-List-item-img-item" :src="item.goodsClassLogo" />
</div>
<div class="classifyNav-List-item-text">{{ item.goodsClassName }}</div>
</div>
</div>
</div>
</template>
<script>
import default_img from "../common/img/placeholder_img.png"
export default {
name: "m-classifyNav",
meta: {
group: "导航组件",
title: "导航组件",
iconfont: "sitemap", // iconfont class name
label: "分类导航",
index: 20,
disabled: false,
},
axes: {
x: 0,
y: 0,
w: 160,
h: 32,
},
data() {
return {
defaultImg: `this.src="${default_img}"` //默认图地址
}
},
watch: {
'option.data': {
handler(newName, oldName) {
let dataLength = newName.length;
if(dataLength > 3 && dataLength < 8 && this.option.rowSwitch == 'double') {
let needLength = 8 - dataLength;
for (let index = 0; index < needLength; index++) {
this.option.data.push({})
}
}
},
immediate: true,
// deep: true
}
},
computed: {
imageStyle() {
let imgSize = this.option.css.imgSize;
let style = { width: imgSize, height: imgSize};
return style;
},
doubleRowstyle() {
let rowSwitch = this.option.rowSwitch;
if(rowSwitch == 'double') {
let style = { marginBottom: '10px'};
return style;
}
},
},
props: {
// 配置
option: {
type: [Object],
default() {
return {
css: {
imgSize: '60px',
color: "#333333",
backgroundColor: "#FFFFFF",
paddingLeft: '30px',
paddingRight: '30px',
paddingTop: '10px',
paddingBottom: '10px',
},
style: {
sidemar: '30px'
},
rowSwitch: 'single',
default: [
{ goodsClassLogo: default_img, goodsClassName: "分类导航", url: "默认链接" },
{ goodsClassLogo: default_img, goodsClassName: "分类导航", url: "默认链接" },
{ goodsClassLogo: default_img, goodsClassName: "分类导航", url: "默认链接" },
{ goodsClassLogo: default_img, goodsClassName: "分类导航", url: "默认链接" },
],
data: [],
};
},
},
source: {
type: [Object],
default() {
return {};
},
},
},
};
</script>
<style lang="less" scoped>
.classifyNav {
&-List,
&-ListFlex {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
.flexLayout{
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
&-item {
width: 22%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
&-img {
&-item {
height: 100%;
width: 100%;
}
}
&-text {
width: 100%;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
margin-top: 5px;
width: 100%;
font-size: 1.4rem;
text-align: center;
}
}
}
&-List {
// &-item {
// margin-right: 2.66%;
// }
// &-item:nth-child(4n) {
// margin-right: 0;
// }
}
&-ListFlex {
justify-content: space-around;
}
}
</style>
<template>
<div class="navigator-wrap">
<kit-taber>
<template #content>
<kit-formic-pack :namespace="namespace">
<kit-formic-part key="classify" title="分类选择">
<kit-formic-item>
<kit-picker-context v-model:value="option.data" :link="fromicLink" :limit="[1,5]" :fileCtype="'img'">
<template v-slot="{ event }">
<a-button type="primary" @click="event.open">请选择分类</a-button>
</template>
</kit-picker-context>
</kit-formic-item>
</kit-formic-part>
<!-- 已选择分类 -->
<kit-formic-part key="classCheked" title="分类内容">
<!-- <div class="rowSwitch">
</div> -->
<div class="classContent">
<span>排版样式</span>
<div>
<a-radio-group v-model:value="option.rowSwitch" button-style="solid">
<a-radio-button value="single">单排</a-radio-button>
<a-radio-button value="double">双排</a-radio-button>
</a-radio-group>
</div>
</div>
<div class="selectedClass">
<div>已选分类(可拖拽切换顺序):</div>
<draggable
v-if="option.data"
v-model="option.data"
@start="drag=true"
@end="drag=false"
item-key="classtreeId"
class="classSelect">
<template #item="{element}">
<div class="classSelect-item" v-if="element.goodsClassLogo || element.goodsClassName">
<div class="classSelect-item-img">
<img :src="element.goodsClassLogo ? element.goodsClassLogo : '' " :onerror="defaultImg">
</div>
<div class="classSelect-item-name">
{{ element.goodsClassName }}
</div>
</div>
</template>
</draggable>
</div>
</kit-formic-part>
</kit-formic-pack>
</template>
<template #style>
<div class="wrap--style">
<kit-formic-pack :namespace="stylespace">
<!-- Part as Form -->
<kit-formic-part key="contentStyle" title="内容样式">
<div class="classContent">
<span>图片尺寸</span>
<div>
<a-col :span="12">
<a-radio-group v-model:value="option.css.imgSize" button-style="solid">
<a-radio-button value="70px"></a-radio-button>
<a-radio-button value="60px"></a-radio-button>
</a-radio-group>
</a-col>
</div>
</div>
<div class="classContent">
<span>文字颜色</span>
<div>
<kit-picker-color
@setColor="onPickerColorText"
:reset="true"
:value="true"
:dColor="option.css.fontColor"
/>
</div>
</div>
<div class="classContent">
<span>背景颜色</span>
<div>
<kit-picker-color
@setColor="onPickerColorBg"
:reset="true"
:value="true"
:dColor="option.css.backgroundColor"
/>
</div>
</div>
</kit-formic-part>
<kit-formic-part key="assemblyStyle" title="组件样式">
<div class="classContent">
<div>
<span class="margin">上边距</span>
<span class="checkedText">{{option.css.paddingTop}}</span>
</div>
<a-col :span="12">
<a-radio-group v-model:value="option.css.paddingTop" button-style="solid">
<a-radio-button value="0"></a-radio-button>
<a-radio-button value="10px"></a-radio-button>
<a-radio-button value="15px"></a-radio-button>
</a-radio-group>
</a-col>
</div>
<div class="classContent">
<div>
<span class="margin">下边距</span>
<span class="checkedText">{{option.css.paddingBottom}}</span>
</div>
<a-col :span="12">
<a-radio-group v-model:value="option.css.paddingBottom" button-style="solid">
<a-radio-button value="0"></a-radio-button>
<a-radio-button value="10px"></a-radio-button>
<a-radio-button value="15px"></a-radio-button>
</a-radio-group>
</a-col>
</div>
<div class="classContent">
<div>
<span class="margin">左右边距</span>
<span class="checkedText">{{option.style.sidemar}}</span>
</div>
<a-col :span="12">
<a-radio-group v-model:value="option.style.sidemar" button-style="solid" @change="onSideMargin">
<a-radio-button value="0"></a-radio-button>
<a-radio-button value="15px"></a-radio-button>
<a-radio-button value="30px"></a-radio-button>
</a-radio-group>
</a-col>
</div>
</kit-formic-part>
</kit-formic-pack>
</div>
</template>
</kit-taber>
</div>
</template>
<script>
import draggable from 'vuedraggable'
import default_img from "../common/img/placeholder_img.png"
import { GOODS_TYPE_LINK } from "../common/constent.js"
export default {
name: "m-classifyNav-optional",
components: {
draggable
},
data() {
return {
namespace: ["classify", "classCheked"],
stylespace: ["contentStyle", 'assemblyStyle'],
defaultImg: `this.src="${default_img}"`, //默认图地址
fromicLink: GOODS_TYPE_LINK
};
},
methods: {
onPickerColorText(color) {
this.option.css.color = color;
},
onPickerColorBg(color) {
this.option.css.backgroundColor = color;
},
onSideMargin(margin){
this.option.css.paddingLeft = margin.target.value;
this.option.css.paddingRight = margin.target.value;
}
},
watch: {
},
props: {
// 配置
option: {
type: [Object],
default() {
return {};
},
},
source: {
type: [Object],
default() {
return {};
},
},
},
};
</script>
<style lang="less" scoped>
.image {
display: block;
width: 100%;
}
.navigator-wrap {
.title {
color: #595961;
font-size: 12px;
margin: 10px 0;
}
.optionCol {
display: flex;
justify-content: space-between;
span {
line-height: 30px;
}
}
.selectedClass {
margin: 10px 0;
.classSelect {
display: flex;
flex-wrap: wrap;
&-item {
width: 23%;
margin-bottom: 10px;
margin-right: 2.66%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
&-img {
width: 50px;
height: 50px;
img {
width: 100%;
height: 100%;
}
}
&-name {
width: 100%;
height: 40px;
padding: 5px 0 10px;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
text-align: center;
}
}
&-item:nth-child(4n) {
margin-right: 0;
}
}
}
.addButton {
display: block;
width: 296px;
margin: 0 auto 50px;
}
.classContent {
display: flex;
justify-content: space-between;
align-items: center;
margin: 15px 0;
.margin {
margin-right: 20px;
}
.checkedText{
color: #9797a1;
}
/deep/ .ant-radio-group {
display: flex;
}
}
.rowSwitch {
margin: 20px 0 10px;
}
}
</style>
//商品列表 pages/goodsList/main?classtreeType=home&classtreeCode=518464173387866177
const GOODS_LIST = '/pages/goodsList/main';
const GOODS_TYPE = '/pages/class_modules/main';
//商品详情 pages/goodsDetail/main?skuCode=556114825435148386
const GOODS_DETAIL = '/pages/goodsDetail/main';
// 自定义页面 /pages/home/main?menuOpcode=index_two&tginfoMenuId=43678
const CUSTOM_LINK = "/pages/home/main";
// 活动
const ACTION_LINK = "/pages/activitylist/main";
//列表
const GOODS_TYPE_LINK = 'https://b2copt-dev2021112700000085.qjclouds.com/paas/cms-manager/index.html#/lowClassifty';
//图片或视频 fileCtype:'video' 视频 fileCtype:'img' 图片
const GOODS_PIC_LINK = 'https://b2copt-dev2021112700000085.qjclouds.com/paas/cms-manager/index.html#/lowFileList ';
// 商品详情
const GOODS_LINK = 'https://b2copt-dev2021112700000085.qjclouds.com/paas/cms-manager/index.html#/goodsList';
export {
GOODS_LIST,
GOODS_TYPE,
GOODS_DETAIL,
GOODS_LINK,
GOODS_TYPE_LINK,
GOODS_PIC_LINK,
CUSTOM_LINK,
ACTION_LINK
};
.hu-color-picker{padding:10px;background:#1d2024;border-radius:4px;box-shadow:0 0 16px 0 rgba(0,0,0,.16);box-sizing: content-box;z-index:1}.hu-color-picker.light{background:#f7f8f9}.hu-color-picker.light .color-show .sucker{background:#eceef0}.hu-color-picker.light .color-type .name{background:#e7e8e9}.hu-color-picker.light .color-type .value{color:#666;background:#eceef0}.hu-color-picker.light .colors.history{border-top:1px solid #eee}.hu-color-picker canvas{vertical-align:top}.hu-color-picker .color-set{display:flex}.hu-color-picker .color-show{margin-top:8px;display:flex}.saturation{position:relative;cursor:pointer}.saturation .slide{position:absolute;left:100px;top:0;width:10px;height:10px;border-radius:50%;border:1px solid #fff;box-shadow:0 0 1px 1px rgba(0,0,0,.3);pointer-events:none}.color-alpha{position:relative;margin-left:8px;cursor:pointer}.color-alpha .slide{position:absolute;left:0;top:100px;width:100%;height:4px;background:#fff;box-shadow:0 0 1px 0 rgba(0,0,0,.3);pointer-events:none}.sucker{width:30px;fill:#9099a4;background:#2e333a;cursor:pointer;transition:all .3s}.sucker.active,.sucker:hover{fill:#1593ff}.hue{position:relative;margin-left:8px;cursor:pointer}.hue .slide{position:absolute;left:0;top:100px;width:100%;height:4px;background:#fff;box-shadow:0 0 1px 0 rgba(0,0,0,.3);pointer-events:none}.colors{padding:0;margin:0}.colors.history{margin-top:10px;border-top:1px solid #2e333a}.colors .item{position:relative;width:16px;height:16px;margin:10px 0 0 10px;border-radius:3px;box-sizing:border-box;vertical-align:top;display:inline-block;transition:all .1s;cursor:pointer}.colors .item:nth-child(8n+1){margin-left:0}.colors .item:hover{transform:scale(1.4)}.colors .item .alpha{height:100%;border-radius:4px}.colors .item .color{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:3px}.color-type{display:flex;margin-top:8px;font-size:12px}.color-type .name{width:60px;height:30px;float:left;display:flex;justify-content:center;align-items:center;color:#999;background:#252930}.color-type .value{flex:1;height:30px;min-width:100px;padding:0 12px;border:0;color:#fff;background:#2e333a;box-sizing:border-box}
<template>
<a-form-item>
<a-select
dropdown-class-name="stop"
v-model:value="formic.content"
:options="contentData"
@change="change"
show-search
optionLabelProp="label"
optionFilterProp="label"
labelInValue
placeholder="搜索字段名称"
></a-select>
</a-form-item>
</template>
<script>
export default {
name: 'fields-select',
props: {
fields: {
type: [Array],
default() {
return [];
}
},
formic: {
type: [Object],
default() {
return {};
},
},
},
methods: {
change(value, option) {
this.$emit('change', value, option);
}
},
computed: {
contentData() {
const data = _.forEach(this.fields, function(item) {
if (item.name) {
item.value = item.name;
}
if (item.options && item.options.length) {
item.options && item.options.forEach(option => {
option.value = option.name;
});
}
});
return data;
}
},
};
</script>
\ No newline at end of file
<template>
<div>
<a-input-group>
<a-row :gutter="6">
<a-col :span="18">
<a-input v-model:value="promotionCode" placeholder="请填写活动代码"/>
</a-col>
<a-col :span="6" style="width: 100%">
<a-button @click="handle">确定</a-button>
</a-col>
</a-row>
</a-input-group>
</div>
</template>
<script>
import {ACTION_LINK} from "../common/constent";
export default {
name: "m-actionLink",
data(){
return {
// 活动代码
promotionCode: ""
}
},
methods: {
handle() {
const result = `${ACTION_LINK}?promotionCode=${_.trim(this.promotionCode)}`
console.log(30, result)
this.$emit("getActionLink", result)
}
}
}
</script>
<style scoped>
</style>
<style lang="less" scoped>
.ant-form-item {
margin-bottom: 8px;
}
</style>
<template>
<m-card :title="title">
<template #content>
<!-- <a-row :gutter="8" v-if="!fluid">
<a-col :span="12">
<a-form-item>
<a-input-number class="input-number-x" :precision="0" v-model:value="mmAxes.x" :step="one.x" :min="1" :max="1000" @change="inputChageX" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item>
<a-input-number class="input-number-y" :precision="0" v-model:value="mmAxes.y" :step="one.y" :min="1" :max="1000" @change="inputChageY" />
</a-form-item>
</a-col>
</a-row> -->
<a-row :gutter="8">
<a-col :span="12">
<a-form-item>
<a-input-number class="input-number-width" :precision="0" :step="one.x" v-model:value="mmAxes.w" :min="1" @change="inputChageW" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item>
<a-input-number class="input-number-height" :precision="0" :step="one.y" v-model:value="mmAxes.h" :min="1" @change="inputChageH" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="8" v-if="showLockscal">
<a-col :span="12">
<a-checkbox v-model:checked="axes.sync" :disabled="disabledLockscal">锁定宽高比</a-checkbox>
</a-col>
</a-row>
</template>
</m-card>
</template>
<script>
import mCard from './m-card';
export default {
mame: 'm-axes',
components: {
mCard,
},
props: {
title: {
type: String,
default: '大小(px)',
},
showLockscal: {
type: Boolean,
default: true,
},
disabledLockscal: {
type: Boolean,
default: false,
},
axes: {
type: [Object],
default() {
return {
x: 0,
y: 0,
w: 0,
h: 0,
sync: false,
};
},
},
},
computed: {
mmAxes() {
return {
x: this.axes.x,
y: this.axes.y,
w: this.axes.w,
h: this.axes.h,
};
},
},
methods: {
inputChageX(e) {
this.mm2px(e, 'x');
},
inputChageY(e) {
this.mm2px(e, 'y');
},
inputChageW(e) {
this.mm2px(e, 'w');
},
inputChageH(e) {
this.mm2px(e, 'h');
},
mm2px(val, e) {
let value = val;
if (!this.isNumber(val)) return;
if ((e == 'w' || e == 'h') && val < 12) {
value = 8;
}
this.axes[e] = value;
},
isNumber(n) {
return typeof n === 'number';
},
},
};
</script>
<style lang="less">
.panel-column {
margin-top: 10px;
margin-bottom: 15px;
&__head {
margin-bottom: 10px;
.title {
margin: 0;
padding-left: 24px;
position: relative;
// font-family: PingFangSC-Medium;
font-size: 14px;
color: rgba(0, 0, 0, 0.85);
letter-spacing: 0;
line-height: 22px;
&::before {
content: "";
width: 4px;
height: 16px;
position: absolute;
top: 3px;
left: 12px;
padding: 0 2px;
background: #00a6c9;
border-radius: 2px;
}
}
}
}
</style>
<template>
<div class="panel-column">
<div class="panel-column__head">
<p class="title">
<template v-if="title">
{{ title }}
</template>
<template v-else>
<slot name="title"></slot>
</template>
</p>
</div>
<div class="panel-column__con">
<slot name="content"></slot>
</div>
</div>
</template>
<script>
export default {
name: "m-card",
props: {
title: {
type: String,
default: "",
},
},
};
</script>
<style lang="less" scoped>
@import url(./color-picker.css);
.btn-con{
display: flex;
align-items: center;
}
.left-content{
margin-right: 4px;
}
.arrow {
display: flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
text-align: center;
color: #d9d9d9
}
.border-color{
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 2px 0 8px;
min-height: 32px;
border: 1px solid #D9D9D9;
border-radius: 4px;
}
</style>
<template>
<a-popover trigger="click" placement="topRight" style="width: 250px;">
<div class="border-color">
<div class="left-content">
<!-- style="{background: componentsColor}" -->
<slot name="content"></slot>
</div>
<div class="arrow">
<svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg>
</div>
</div>
<template #content>
<ColorPicker
theme="light"
:color="color"
:sucker-hide="true"
:sucker-area="suckerArea"
@changeColor="changeColor"
/>
</template>
</a-popover>
</template>
<script>
import { ref, reactive} from 'vue';
// import { DownOutlined } from '@ant-design/icons-vue';
import { ColorPicker } from 'vue-color-kit'
export default {
name: 'm-color-picker',
components: {
ColorPicker,
},
props: {
color: {
type: String,
default: '#000'
}
},
data() {
return {
suckerCanvas: null,
suckerArea: [],
componentsColor: '#000'
}
},
methods: {
changeColor(color) {
const { r, g, b, a } = color.rgba
this.componentsColor = `rgba(${r}, ${g}, ${b}, ${a})`
this.$emit('onChange', this.componentsColor);
}
}
};
</script>
\ No newline at end of file
<style lang="less" scoped></style>
<template>
<a-row :gutter="[0, 16]" type="flex" justify="space-around" align="middle">
<a-col v-if="value[0]" :span="isShowPx?5:8"> 上边距 </a-col>
<a-col :span="3" v-if="isShowPx&&value[0]"> {{ topMargins }} </a-col>
<a-col v-if="value[0]" :span="16">
<a-radio-group v-model:value="topMargins" button-style="solid" @change="changeTopMargins">
<a-radio-button :value="item" v-for="(item, key) in value[0]">{{ key }}</a-radio-button>
</a-radio-group>
</a-col>
<a-col v-if="value[1]" :span="isShowPx?5:8"> 下边距 </a-col>
<a-col :span="3" v-if="isShowPx&&value[1]"> {{ bottomMargins }} </a-col>
<a-col v-if="value[1]" :span="16">
<a-radio-group v-model:value="bottomMargins" button-style="solid" @change="changeBottomMargins">
<a-radio-button :value="item" v-for="(item, key) in value[1]">{{ key }}</a-radio-button>
</a-radio-group>
</a-col>
<a-col v-if="value[2]" :span="isShowPx?5:8"> 左边距 </a-col>
<a-col :span="3" v-if="isShowPx&&value[2]"> {{ leftMargins }} </a-col>
<a-col v-if="value[2]" :span="16">
<a-radio-group v-model:value="leftMargins" button-style="solid" @change="changeLeftMargins">
<a-radio-button :value="item" v-for="(item, key) in value[2]">{{ key }}</a-radio-button>
</a-radio-group>
</a-col>
<a-col v-if="value[3]" :span="isShowPx?5:8"> 右边距 </a-col>
<a-col :span="3" v-if="isShowPx&&value[3]"> {{ rightMargins }} </a-col>
<a-col v-if="value[3]" :span="16">
<a-radio-group v-model:value="rightMargins" button-style="solid" @change="changeRightMargins">
<a-radio-button :value="item" v-for="(item, key) in value[3]">{{ key }}</a-radio-button>
</a-radio-group>
</a-col>
</a-row>
</template>
<script>
/* <m-component-style
v-model:topMargins="option.css.paddingTop" //上边距
v-model:bottomMargins="option.css.paddingBottom" //下边距
v-model:leftMargins="option.css.paddingLeft" //左边距
v-model:rightMargins="option.css.paddingRight" //右边距
:isShowPx="true" //是否显示当前选中的值
:value="[ //上下左右,四个边距radio的value&key
{ 小: '0px', 中: '8px', 大: '16px'},
{ 小: '0px', 中: '8px', 大: '16px'},
{ 小: '0px', 中: '8px', 大: '16px'},
{ 小: '0px', 中: '8px', 大: '16px'},
]"
></m-component-style>
*/
export default {
name: 'm-component-style',
props: {
topMargins: {
type: String,
default: '0px',
},
bottomMargins: {
type: String,
default: '0px',
},
leftMargins: {
type: String,
default: '0px',
},
rightMargins: {
type: String,
default: '0px',
},
isShowPx: {
type: Boolean,
default: true,
},
value: {
type: Object,
default: [
{ : '0px', : '8px', : '16px' },
{ : '0px', : '8px', : '16px' },
],
},
},
computed: {},
methods: {
changeTopMargins(e) {
this.$emit('update:topMargins', e.target.value);
},
changeBottomMargins(e) {
this.$emit('update:bottomMargins', e.target.value);
},
changeLeftMargins(e) {
this.$emit('update:leftMargins', e.target.value);
},
changeRightMargins(e) {
this.$emit('update:rightMargins', e.target.value);
},
},
};
</script>
<template>
<div>
<a-input-group>
<a-row :gutter="6">
<a-col :span="9">
<a-input v-model:value="menuOpcode" placeholder="栏目标识"/>
</a-col>
<a-col :span="9">
<a-input v-model:value="tginfoMenuId" placeholder="栏目ID"/>
</a-col>
<a-col :span="6" style="width: 100%">
<a-button @click="handle">确定</a-button>
</a-col>
</a-row>
</a-input-group>
</div>
</template>
<script>
import { CUSTOM_LINK } from "../common/constent";
export default {
name: "m-customLink",
data(){
return {
// 栏目标识
menuOpcode: "",
// 模版ID
tginfoMenuId: ""
}
},
methods: {
handle() {
const result = `${CUSTOM_LINK}?menuOpcode=${_.trim(this.menuOpcode)}&tginfoMenuId=${_.trim(this.tginfoMenuId)}`
this.$emit("getCustomLink", result)
}
}
}
</script>
<style lang="less" scoped>
[class^='input-number'] {
position: relative;
width: 100%;
&:after {
top: 0;
right: 0;
position: absolute;
color: rgba(0, 0, 0, 0.25);
line-height: 30px;
padding-right: 4px;
}
}
.input-number-size {
&:after {
content: '楼层宽度占比';
}
}
</style>
<template>
<mCard>
<template #title>
<span style="margin-right: 5px;">楼层宽度占比</span>
<a-tooltip placement="bottom">
<template #title>
<span>楼层内不同组件宽度占比</span>
</template>
<InfoCircleFilled style="color: #00A6C9;" />
</a-tooltip>
</template>
<template #content>
<a-row :gutter="8">
<a-col :span="24">
<a-form-item>
<a-input-number class="input-number-size" v-model:value="css.flex" :min="1" :max="100"></a-input-number>
</a-form-item>
</a-col>
</a-row>
</template>
</mCard>
</template>
<script>
import mCard from './m-card'
export default {
mame: 'm-flex',
components: {
mCard
},
props: {
title: {
type: String,
default: '栏位'
},
css: {
type: [Object],
default() {
return {
flex: 1
}
}
},
},
}
</script>
<style lang="less">
.content {
margin: 0 auto;
width: 90%;
}
[class^="input-number"] {
position: relative;
width: 100%;
&:after {
top: 0;
right: 0;
position: absolute;
color: rgba(0, 0, 0, 0.25);
line-height: 30px;
padding-right: 4px;
}
}
.input-number-size-ls {
&:after {
content: "字距";
}
}
.input-number-size-lh {
&:after {
content: "行距";
}
}
.input-number-x {
&:after {
content: "X";
}
}
.input-number-y {
&:after {
content: "Y";
}
}
.input-number-width {
&:after {
content: "宽";
}
}
.input-number-height {
&:after {
content: "高";
}
}
.font-style-box {
display: flex;
height: 32px;
line-height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
.item {
flex: 1;
text-align: center;
cursor: pointer;
&.active {
color: #00a6c9;
}
}
}
.ant-radio-group {
display: flex;
width: 100%;
.ant-radio-button-wrapper {
flex: 1;
text-align: center;
}
}
.font_color {
position: relative;
line-height: 1.1;
text-align: center;
.bg {
display: block;
width: 16px;
height: 4px;
border-radius: 4px;
border: 1px solid #d9d9d9;
box-sizing: content-box;
background: #fff;
}
}
.font_bg {
position: relative;
line-height: 1.1;
text-align: center;
.font {
position: relative;
z-index: 10;
}
.bg {
display: block;
border: 1px solid #d9d9d9;
margin-top: -6px;
width: 16px;
height: 11px;
border-radius: 4px;
box-sizing: content-box;
background: #fff;
z-index: 1;
}
}
.ant-form-item {
margin-bottom: 8px;
}
</style>
<template>
<mCard title="样式设置">
<template #content>
<a-form class="content" layout="vertical" :model="option">
<!-- <a-form-item label="文字方向">
<a-radio-group v-model:value="option.writingMode">
<a-radio-button value="horizontal-tb">横向</a-radio-button>
<a-radio-button value="vertical-lr">竖向</a-radio-button>
</a-radio-group>
</a-form-item> -->
<a-form-item>
<a-select
v-model:value="option.textType"
:options="textTypeOptions"
@change="changeTextType"
></a-select>
</a-form-item>
<a-row :gutter="8">
<a-col :span="12">
<a-form-item>
<a-select
v-model:value="option.fontFamily"
:options="fontFamilyOptions"
></a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item>
<a-select
v-model:value="option.fontSize"
:options="fontSizeOptions"
></a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="8">
<a-col :span="12">
<div class="font-style-box">
<div
class="item"
:class="{ active: option.fontWeight !== 'normal' }"
@click="onSelectFontWeight"
>
<visualGuiIcon mode="zitijiacu"></visualGuiIcon>
</div>
<div
class="item"
:class="{ active: option.fontStyle !== 'normal' }"
@click="onSelectFontStyle"
>
<visualGuiIcon mode="zitixieti"></visualGuiIcon>
</div>
<div
class="item"
:class="{ active: option.textDecoration.includes('underline') }"
@click="onSelectTextUnderLine"
>
<visualGuiIcon mode="zitixiahuaxian"></visualGuiIcon>
</div>
<div
class="item"
:class="{
active: option.textDecoration.includes('line-through'),
}"
@click="onSelectTextlineThrough"
>
<visualGuiIcon mode="zitishanchuxian"></visualGuiIcon>
</div>
</div>
</a-col>
<a-col :span="6">
<a-form-item>
<mColorPickerVue :color="option.color" @onChange="changeColor">
<template #content>
<div class="font_color">
A
<span
class="bg"
:style="{ background: option.color }"
></span>
</div>
</template>
</mColorPickerVue>
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item>
<mColorPickerVue
:color="option.backgroundColor"
@onChange="changeBgColor"
>
<template #content>
<div class="font_bg">
<span class="font">ab</span>
<span
class="bg"
:style="{ background: option.backgroundColor }"
></span>
</div>
</template>
</mColorPickerVue>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="8">
<a-col :span="12">
<a-form-item>
<a-input-number
class="input-number-size-ls"
v-model:value="option.letterSpace"
:maxlength="5"
>
</a-input-number>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item>
<a-input-number
class="input-number-size-lh"
v-model:value="option.lineHeight"
:maxlength="5"
>
</a-input-number>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="8">
<a-col :span="24">
<a-radio-group v-model:value="option.textAlign">
<a-radio-button value="left">
<visualGuiIcon mode="zitizuoduiqi"></visualGuiIcon>
</a-radio-button>
<a-radio-button value="center">
<visualGuiIcon mode="zitijuzhong"></visualGuiIcon>
</a-radio-button>
<a-radio-button value="right">
<visualGuiIcon mode="zitiyouduiqi"></visualGuiIcon>
</a-radio-button>
<a-radio-button value="justify">
<visualGuiIcon mode="zitipingjunfenbu"></visualGuiIcon>
</a-radio-button>
</a-radio-group>
</a-col>
</a-row>
</a-form>
</template>
</mCard>
</template>
<script>
import { ref, reactive } from "vue";
import mCard from "./m-card";
import mColorPickerVue from "../components/m-color-picker.vue";
// 文本类型和默认样式
const textTypeOptions = [
{ label: "标题", value: "1" },
{ label: "副标题", value: "2" },
{ label: "正文", value: "3" },
];
const textTypeStyle = [
{
fontFamily: "PingFangSC-Regular,Microsoft YaHei",
fontSize: "16px",
fontWeight: "bold",
color: "#000",
},
{
fontFamily: "PingFangSC-Regular,Microsoft YaHei",
fontSize: "14px",
fontWeight: "bold",
color: "#000",
},
{
fontFamily: "PingFangSC-Regular,Microsoft YaHei",
fontSize: "12px",
fontWeight: "normal",
color: "#000",
},
];
const fontSizeOptions = [
{ value: "12px", label: "12px" },
{ value: "14px", label: "14px" },
{ value: "16px", label: "16px" },
{ value: "18px", label: "18px" },
{ value: "20px", label: "20px" },
{ value: "22px", label: "22px" },
{ value: "24px", label: "24px" },
{ value: "26px", label: "26px" },
];
const fontFamilyOptions = [
{ value: "Microsoft YaHei", label: "微软雅黑" },
{ value: "SimHei", label: "黑体" },
{ value: "SimSong", label: "宋体" },
{ label: "仿宋", value: "仿宋" },
{ label: "楷体", value: "楷体" },
{ label: "隶书", value: "隶书" },
{ label: "幼圆", value: "幼圆" },
{ value: "fangsong", label: "fangsong" },
{ value: "cursive", label: "cursive" },
{ value: "serif", label: "serif" },
{ value: "monospace", label: "monospace" },
{ value: "system-ui;", label: "system-ui;" },
{ value: "ui-serif;", label: "ui-serif;" },
{ value: "ui-sans-serif", label: "ui-sans-serif" },
{ value: "ui-monospace", label: "ui-monospace" },
{ value: "ui-rounded", label: "ui-rounded" },
{ value: "emoji", label: "emoji" },
{ value: "math", label: "math" },
{ value: "inherit", label: "inherit" },
{ value: "initial", label: "initial" },
{ value: "revert", label: "revert" },
{ value: "unset", label: "unset" },
{ label: "Andale Mono", value: "'andale mono', times" },
{ label: "Arial", value: "arial, helvetica, sans-serif" },
{ label: "Arial Black", value: "'arial black', 'avant garde'" },
{ label: "Book Antiqua", value: "'book antiqua', palatino" },
{ label: "Comic Sans MS", value: "'comic sans ms', sans-serif" },
{ label: "Courier New", value: "'courier new', courier" },
{ label: "Georgia", value: "georgia, palatino" },
{ label: "Helvetica", value: "helvetica" },
{ label: "Impact", value: "impact, chicago" },
{ label: "Symbol", value: "symbol" },
{ label: "Tahoma", value: "tahoma, arial, helvetica, sans-serif" },
{ label: "Terminal", value: "terminal, monaco" },
{ label: "Times New Roman", value: "'times new roman', times" },
{ label: "Trebuchet MS", value: "'trebuchet ms', geneva" },
{ label: "Verdana", value: "verdana, geneva" },
{ label: "Webdings", value: "Webdings" },
{ label: "Wingdings", value: "Wingdings" },
];
export default {
name: "m-font",
components: {
mCard,
mColorPickerVue,
},
props: {
option: {
type: [Object],
default() {
return {};
},
},
},
data() {
return {
textTypeOptions,
textTypeStyle,
fontFamilyOptions,
fontSizeOptions,
textTypeStyle,
};
},
methods: {
changeTextType(value) {
const { fontFamily, fontSize, fontWeight } = textTypeStyle[value - 1];
this.option.fontFamily = fontFamily;
this.option.fontSize = fontSize;
this.option.fontWeight = fontWeight;
},
onSelectFontWeight() {
this.option.fontWeight =
this.option.fontWeight === "normal" ? "bold" : "normal";
},
onSelectFontStyle() {
this.option.fontStyle =
this.option.fontStyle === "normal" ? "italic" : "normal";
},
onSelectTextUnderLine() {
this.addLine("underline");
},
addLine(type) {
const style = this.option.textDecoration;
let underline, lineThrough;
if (type === "underline") {
underline = style.includes("underline") ? "" : "underline";
lineThrough = style.includes("line-through") ? "line-through" : "";
} else {
underline = style.includes("underline") ? "underline" : "";
lineThrough = style.includes("line-through") ? "" : "line-through";
}
this.option.textDecoration = `${underline} ${lineThrough} auto`;
console.log(this.option.textDecoration);
},
onSelectTextlineThrough() {
this.addLine("line-through");
},
changeColor(color) {
this.option.color = color;
},
changeBgColor(color) {
this.option.backgroundColor = color;
},
onSelectDelLine() {
this.option.delLine = !this.option.delLine;
},
},
};
</script>
<style lang="less" scoped>
[class^='padding'] {
position: relative;
width: 100%;
&:after {
top: 0;
right: 0;
position: absolute;
color: rgba(0, 0, 0, 0.25);
line-height: 30px;
padding-right: 4px;
}
}
.padding-top{
&:after {
content: '上';
}
}
.padding-bottom{
&:after {
content: '下';
}
}
.padding-left{
&:after {
content: '左';
}
}
.padding-right{
&:after {
content: '右';
}
}
</style>
<template>
<mCard>
<template #title>
<span style="margin-right: 5px;">组件内边距(px)</span>
<a-tooltip placement="bottom">
<template #title>
<span>组件边线到组件内容的距离</span>
</template>
<InfoCircleFilled style="color: #00A6C9;" />
</a-tooltip>
</template>
<template #content>
<a-row :gutter="8">
<a-col :span="12">
<a-form-item>
<a-input-number class="padding-top" v-model:value="padding.top"></a-input-number>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item>
<a-input-number class="padding-bottom" v-model:value="padding.bottom"></a-input-number></a-form-item>
</a-col>
<a-col :span="12">
<a-form-item>
<a-input-number class="padding-left" v-model:value="padding.left"></a-input-number></a-form-item>
</a-col>
<a-col :span="12">
<a-form-item>
<a-input-number class="padding-right" v-model:value="padding.right"></a-input-number></a-form-item>
</a-col>
</a-row>
</template>
</mCard>
</template>
<script>
import mCard from './m-card'
export default {
mame: 'm-flex',
components: {
mCard
},
props: {
title: {
type: String,
default: '边距'
},
padding: {
type: [Object],
default() {
return {
top: 0,
left: 0,
right: 0,
bottom: 0
}
}
},
},
}
</script>
<template>
<m-card :title="title">
<template #content>
<a-row :gutter="8">
<a-col :span="12">
<a-form-item>
<a-input-number
class="input-number-width stop"
:precision="0"
:step="1"
v-model:value="size.width"
@change="handleChangeWidth"
:min="1"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item>
<a-input-number
class="input-number-height stop"
:precision="0"
:step="1"
v-model:value="size.height"
@change="handleChangeHeight"
:min="1"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="8" v-if="showLockscal">
<a-col :span="12">
<a-checkbox
v-model:checked="isSync"
:disabled="disabledLockscal"
@change="lockChange"
>锁定宽高比</a-checkbox>
</a-col>
</a-row>
</template>
</m-card>
</template>
<script>
import mCard from './m-card.vue';
export default {
name: 'm-size',
components: {
mCard,
},
props: {
title: {
type: String,
default: '大小(px)',
},
size: {
type: Object,
default() {
return {
width: 0,
height: 0,
}
},
},
showLockscal: {
type: Boolean,
default: true,
},
isSync: {
type: Boolean,
default: false,
},
disabledLockscal: {
type: Boolean,
default: false,
},
width: {
type: Number,
default: 0,
},
height: {
type: Number,
default: 0,
},
},
data() {
return {
width: this.size.width,
height: this.size.height,
}
},
methods: {
handleChange(value) {
if (this.isSync) {
this.size.width = value;
this.size.height = value;
}
},
lockChange(event) {
console.log(event.target.value)
const { checked } = event.target;
if (checked) {
this.width = this.size.width;
this.height = this.size.height;
}
},
handleChangeWidth(value) {
if (!this.isNumber(value)) return;
if (this.isSync) {
const { width, height } = this;
const proportion = height / width;
this.size.width = value;
this.size.height = parseInt(proportion * value);
}
},
handleChangeHeight(value) {
if (!this.isNumber(value)) return;
if (this.isSync) {
const { width, height } = this;
const proportion = width / height;
this.size.width = parseInt(proportion * value);
this.size.height = value;
}
},
isNumber(n) {
return typeof n === 'number';
},
},
}
</script>
<style lang="less" scoped>
[class^='input-number'] {
position: relative;
width: 100%;
&:after {
top: 0;
right: 0;
position: absolute;
color: rgba(0, 0, 0, 0.25);
line-height: 30px;
padding-right: 4px;
}
}
.input-number-width {
&:after {
content: '宽';
}
}
.input-number-height {
&:after {
content: '高';
}
}
</style>
\ No newline at end of file
<style lang="less" scoped>
[class^='input-number'] {
position: relative;
&:after {
top: 0;
right: 0;
position: absolute;
color: rgba(0, 0, 0, 0.25);
line-height: 30px;
padding-right: 4px;
}
}
.input-number-size-ls {
&:after {
content: '字距';
}
}
.input-number-size-lh {
&:after {
content: '行距';
}
}
.input-number-x {
&:after {
content: 'X';
}
}
.input-number-y {
&:after {
content: 'Y';
}
}
.input-number-width {
&:after {
content: '宽';
}
}
.input-number-height {
&:after {
content: '高';
}
}
.font-style-box {
display: flex;
margin-bottom: 15px;
height: 32px;
line-height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
.item {
flex: 1;
text-align: center;
cursor: pointer;
&.active {
color: #00a6c9;
}
}
}
.ant-radio-group {
display: flex;
width: 100%;
.ant-radio-button-wrapper {
flex: 1;
text-align: center;
}
}
.font_color {
position: relative;
line-height: 1.1;
text-align: center;
.bg {
display: block;
width: 16px;
height: 4px;
border-radius: 4px;
border: 1px solid #d9d9d9;
box-sizing: content-box;
background: #fff;
}
}
.font_bg {
position: relative;
line-height: 1.1;
text-align: center;
.font {
position: relative;
z-index: 10;
}
.bg {
display: block;
border: 1px solid #d9d9d9;
margin-top: -6px;
width: 16px;
height: 11px;
border-radius: 4px;
box-sizing: content-box;
background: #fff;
z-index: 1;
}
}
:deep(.ant-input-affix-wrapper .ant-input-suffix) {
color: rgba(0, 0, 0, 0.25);
}
</style>
<template>
<a-form layout="vertical" :model="option">
<a-row :gutter="8" v-if="!option.isTableRow && !option.isTableHeaderRow">
<a-col :span="12">
<a-form-item>
<a-input-number
class="input-number-width"
v-model:value="option.widthNumber"
:formatter="formatValue"
:step="mmOne.x"
:min="0"
:maxlength="5"
> </a-input-number>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item>
<a-input-number
class="input-number-height"
v-model:value="option.heightNumber"
:formatter="formatValue"
:step="mmOne.y"
:min="0"
:maxlength="5"
@change="handleStyleValueChange"
> </a-input-number>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="8">
<a-col :span="12">
<a-form-item>
<a-select v-model:value="option.fontFamily" :options="fontFamilyOptions"></a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item>
<a-select v-model:value="option.fontSize" :options="fontSizeOptions" @change="handleStyleValueChange"></a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="8">
<a-col :span="12">
<div class="font-style-box">
<div class="item" :class="{ active: option.fontWeight !== 'normal' }" @click="onSelectFontWeight">
<visualGuiIcon mode="zitijiacu"></visualGuiIcon>
</div>
<div class="item" :class="{ active: option.fontStyle !== 'normal' }" @click="onSelectFontStyle">
<visualGuiIcon mode="zitixieti"></visualGuiIcon>
</div>
<div class="item" :class="{ active: option.textDecoration.includes('underline') }" @click="onSelectTextUnderLine">
<visualGuiIcon mode="zitixiahuaxian"></visualGuiIcon>
</div>
<div class="item" :class="{ active: option.textDecoration.includes('line-through')}" @click="onSelectTextlineThrough">
<visualGuiIcon mode="zitishanchuxian"></visualGuiIcon>
</div>
</div>
</a-col>
<a-col :span="6">
<a-form-item>
<mColorPickerVue :color="option.color" @onChange="changeColor">
<template #content>
<div class="font_color">
A
<span class="bg" :style="{background: option.color}"></span>
</div>
</template>
</mColorPickerVue>
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item>
<mColorPickerVue :color="option.backgroundColor" @onChange="changeBgColor">
<template #content>
<div class="font_bg">
<span class="font">ab</span>
<span class="bg" :style="{background: option.backgroundColor}"></span>
</div>
</template>
</mColorPickerVue>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="8">
<a-col :span="12">
<a-form-item>
<a-input-number class="input-number-size-ls" v-model:value="option.letterSpace" :maxlength="5"> </a-input-number>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item>
<a-input-number class="input-number-size-lh" v-model:value="option.lineHeight" :maxlength="5" @change="handleStyleValueChange"> </a-input-number>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="8">
<a-col :span="24">
<a-form-item>
<a-radio-group v-model:value="option.textAlign">
<a-radio-button value="left">
<visualGuiIcon mode="zitizuoduiqi"></visualGuiIcon>
</a-radio-button>
<a-radio-button value="center">
<visualGuiIcon mode="zitijuzhong"></visualGuiIcon>
</a-radio-button>
<a-radio-button value="right">
<visualGuiIcon mode="zitiyouduiqi"></visualGuiIcon>
</a-radio-button>
<a-radio-button value="justify">
<visualGuiIcon mode="zitipingjunfenbu"></visualGuiIcon>
</a-radio-button>
</a-radio-group>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="8" v-if="false">
<a-col :span="8">
<a-form-item>
<a-checkbox v-model:checked="option.isBorderBool">边框</a-checkbox>
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item>
<mColorPickerVue @onChange="changeborderColor" :color="option.borderColor">
<template #content><div class="color-block" :style="{background: option.borderColor}"></div></template>
</mColorPickerVue>
</a-form-item>
</a-col>
<a-col :span="10">
<a-form-item>
<a-input-number class="input-number-borderWidth" v-model:value="option.borderWidthNumber" :min="0" :max="100" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</template>
<script>
import mCard from './m-card'
import mColorPickerVue from '../components/m-color-picker.vue';
const fontSizeOptions = [
{ value: '12px', label: '12px' },
{ value: '14px', label: '14px' },
{ value: '16px', label: '16px' },
{ value: '18px', label: '18px' },
{ value: '20px', label: '20px' },
{ value: '22px', label: '22px' },
{ value: '24px', label: '24px' },
{ value: '26px', label: '26px' }
];
const fontFamilyOptions = [
{ value: 'Microsoft YaHei', label: '微软雅黑' },
{ value: 'SimHei', label: '黑体' },
{ value: 'SimSong', label: '宋体' },
{ label: "仿宋", value: "仿宋"},
{ label: "楷体", value: "楷体"},
{ label: "隶书", value: "隶书"},
{ label: "幼圆", value: "幼圆"},
{ value: 'fangsong', label: 'fangsong' },
{ value: 'cursive', label: 'cursive' },
{ value: 'serif', label: 'serif' },
{ value: 'monospace', label: 'monospace' },
{ value: 'system-ui;', label: 'system-ui;' },
{ value: 'ui-serif;', label: 'ui-serif;' },
{ value: 'ui-sans-serif', label: 'ui-sans-serif' },
{ value: 'ui-monospace', label: 'ui-monospace' },
{ value: 'ui-rounded', label: 'ui-rounded' },
{ value: 'emoji', label: 'emoji' },
{ value: 'math', label: 'math' },
{ value: 'inherit', label: 'inherit' },
{ value: 'initial', label: 'initial' },
{ value: 'revert', label: 'revert' },
{ value: 'unset', label: 'unset' },
{ label: "Andale Mono", value: "'andale mono', times"},
{ label: "Arial", value: "arial, helvetica, sans-serif"},
{ label: "Arial Black", value: "'arial black', 'avant garde'"},
{ label: "Book Antiqua", value: "'book antiqua', palatino"},
{ label: "Comic Sans MS", value: "'comic sans ms', sans-serif"},
{ label: "Courier New", value: "'courier new', courier"},
{ label: "Georgia", value: "georgia, palatino"},
{ label: "Helvetica", value: "helvetica"},
{ label: "Impact", value: "impact, chicago"},
{ label: "Symbol", value: "symbol"},
{ label: "Tahoma", value: "tahoma, arial, helvetica, sans-serif"},
{ label: "Terminal", value: "terminal, monaco"},
{ label: "Times New Roman", value: "'times new roman', times"},
{ label: "Trebuchet MS", value: "'trebuchet ms', geneva"},
{ label: "Verdana", value: "verdana, geneva"},
{ label: "Webdings", value: "Webdings"},
{ label: "Wingdings", value: "Wingdings"},
];
export default {
name: 'm-table-style-setting',
components: {
mCard,
mColorPickerVue
},
props: {
option: {
type: [Object],
default() {
return {}
}
},
parentOption: {
type: [Object],
default() {
return {}
}
}
},
data() {
return {
fontFamilyOptions,
fontSizeOptions,
borderColor: '',
borderWidth: '',
backgroundColor: '',
}
},
methods: {
handleStyleValueChange(value) {
this.parentOption.formic.changedStyleValue = value;
},
formatValue(value) {
if (value) {
return `${Math.floor(value)}`
}
return null;
},
onSelectFontWeight() {
this.option.fontWeight = this.option.fontWeight === 'normal' ? 'bold' : 'normal';
},
onSelectFontStyle() {
this.option.fontStyle = this.option.fontStyle === 'normal' ? 'italic' : 'normal';
},
onSelectTextUnderLine() {
this.addLine('underline');
},
onSelectTextlineThrough() {
this.addLine('line-through');
},
addLine(type) {
const style = this.option.textDecoration;
let underline, lineThrough;
if(type === 'underline') {
underline = style.includes('underline') ? "" : "underline";
lineThrough = style.includes('line-through') ? "line-through" : "";
} else {
underline = style.includes('underline') ? "underline" : "";
lineThrough = style.includes('line-through') ? "" : "line-through";
}
this.option.textDecoration = `${underline} ${lineThrough} auto`;
},
changeColor(color) {
this.option.color=color;
},
changeBgColor(color) {
this.option.backgroundColor = color
},
changebgColor(color) {
this.option.backgroundColor = color;
},
changeborderColor(color) {
this.option.borderColor = color;
}
},
computed: {
mmOne() {
return {
x: this.visualUtils.px2mm(this.one.x),
y: this.visualUtils.px2mm(this.one.y)
}
},
},
mounted() {
},
};
</script>
<style lang="less" scoped>
</style>
<template>
<div v-html="toothChart"> </div>
</template>
<script>
import * as _ from 'lodash';
// 牙位图
const toothRenderMap = [
[
{ code: '55', name: 'E', quadrant: 1 },
{ code: '54', name: 'D', quadrant: 1 },
{ code: '53', name: 'C', quadrant: 1 },
{ code: '52', name: 'B', quadrant: 1 },
{ code: '51', name: 'A', quadrant: 1 },
],
[
{ code: '61', name: 'A', quadrant: 2 },
{ code: '62', name: 'B', quadrant: 2 },
{ code: '63', name: 'C', quadrant: 2 },
{ code: '64', name: 'D', quadrant: 2 },
{ code: '65', name: 'E', quadrant: 2 },
],
[
{ code: '18', name: '8', quadrant: 1 },
{ code: '17', name: '7', quadrant: 1 },
{ code: '16', name: '6', quadrant: 1 },
{ code: '15', name: '5', quadrant: 1 },
{ code: '14', name: '4', quadrant: 1 },
{ code: '13', name: '3', quadrant: 1 },
{ code: '12', name: '2', quadrant: 1 },
{ code: '11', name: '1', quadrant: 1 },
],
[
{ code: '21', name: '1', quadrant: 2 },
{ code: '22', name: '2', quadrant: 2 },
{ code: '23', name: '3', quadrant: 2 },
{ code: '24', name: '4', quadrant: 2 },
{ code: '25', name: '5', quadrant: 2 },
{ code: '26', name: '6', quadrant: 2 },
{ code: '27', name: '7', quadrant: 2 },
{ code: '28', name: '8', quadrant: 2 },
],
[
{ code: '48', name: '8', quadrant: 4 },
{ code: '47', name: '7', quadrant: 4 },
{ code: '46', name: '6', quadrant: 4 },
{ code: '45', name: '5', quadrant: 4 },
{ code: '44', name: '4', quadrant: 4 },
{ code: '43', name: '3', quadrant: 4 },
{ code: '42', name: '2', quadrant: 4 },
{ code: '41', name: '1', quadrant: 4 },
],
[
{ code: '31', name: '1', quadrant: 3 },
{ code: '32', name: '2', quadrant: 3 },
{ code: '33', name: '3', quadrant: 3 },
{ code: '34', name: '4', quadrant: 3 },
{ code: '35', name: '5', quadrant: 3 },
{ code: '36', name: '6', quadrant: 3 },
{ code: '37', name: '7', quadrant: 3 },
{ code: '38', name: '8', quadrant: 3 },
],
[
{ code: '85', name: 'E', quadrant: 4 },
{ code: '84', name: 'D', quadrant: 4 },
{ code: '83', name: 'C', quadrant: 4 },
{ code: '82', name: 'B', quadrant: 4 },
{ code: '81', name: 'A', quadrant: 4 },
],
[
{ code: '71', name: 'A', quadrant: 3 },
{ code: '72', name: 'B', quadrant: 3 },
{ code: '73', name: 'C', quadrant: 3 },
{ code: '74', name: 'D', quadrant: 3 },
{ code: '75', name: 'E', quadrant: 3 },
],
];
const tableStyle = 'style="border-collapse: collapse;"';
const supernumeraryStyle = 'style="font-size: 12px;position: relative;top: -4px;transform: scale(.9);display: inline-block;color: #000;"';
const sidesStyle = 'style="font-size: 12px;position: relative;top: -4px;transform: scale(.9);display: inline-block;color: #000;"';
const quadrant1Style = 'style="border-right: 1px solid #000;border-bottom: 1px solid #000;text-align: right;min-width: 50px;padding: 0;color: #000;"';
const quadrant2Style = 'style="border-left: 1px solid #000;border-bottom: 1px solid #000;text-align: left;min-width: 50px;padding: 0;color: #000;"';
const quadrant3Style = 'style="border-left: 1px solid #000;border-top: 1px solid #000;text-align: left;min-width: 50px;padding: 0;color: #000;"';
const quadrant4Style = 'style="border-right: 1px solid #000;border-top: 1px solid #000;text-align: right;min-width: 50px;padding: 0;color: #000;"';
const flattenedToothRenderMap = _.flatten(toothRenderMap);
export default {
name: 'm-tooth-chart',
props: {
option: {
type: [Object],
default() {
return {
}
}
},
selectedToothCodes: {
type: [String, Array[String]],
default() {
return ''
}
}
},
data() {
return { }
},
methods: {
getToothSelectorHtml(selectedToothCodes, isLittleToothPrint, useInlineStyle) {
const teeth = this.parseCodes(selectedToothCodes);
const quadrants = this.getQuarantHtmls(teeth, useInlineStyle);
return this.generateFinalHtml(quadrants, isLittleToothPrint, useInlineStyle);
},
parseCodes(selectedToothCodes) {
const teeth = {};
if (_.isString(selectedToothCodes)) {
selectedToothCodes = selectedToothCodes.split(',');
}
_.forEach(selectedToothCodes, (code) => {
if (code) {
const toothCode = code.substr(0, 2);
teeth[toothCode] = {};
let sides = code.substr(2).split('_');
if (sides.length === 1 && sides[0] === '') {
sides = [];
} else {
// 剔除多生牙
if (sides.indexOf('LS') > -1) {
sides = _.without(sides, 'LS');
teeth[toothCode].supernumerary = 'left';
} else if (sides.indexOf('RS') > -1) {
sides = _.without(sides, 'RS');
teeth[toothCode].supernumerary = 'right';
}
}
teeth[toothCode].sides = sides;
teeth[toothCode].tooth = _.find(flattenedToothRenderMap, item => {
return item.code === toothCode;
});
}
});
return teeth;
},
getQuarantHtmls(teeth, useInlineStyle) {
const quadrants = { 1: {}, 2: {}, 3: {}, 4: {} };
_.forEach(teeth, (toothItem, toothCode) => {
const sides = toothItem.sides.join(',');
let main;
if (toothItem.supernumerary === 'left') {
// eslint-disable-next-line max-len
main = `<div style="display: inline-block">(<span class="supernumerary" ${useInlineStyle ? supernumeraryStyle : ''}>S</span><span>${toothItem.tooth.name}</span>)</div>`;
} else if (toothItem.supernumerary === 'right') {
// eslint-disable-next-line max-len
main = `<div style="display: inline-block">(<span>${toothItem.tooth.name}</span><span class="supernumerary" ${useInlineStyle ? supernumeraryStyle : ''}>S</span>)</div>`;
} else {
main = `<div style="display: inline-block;"><span>${toothItem.tooth.name}</span>`;
}
quadrants[toothItem.tooth.quadrant][toothItem.tooth.name] = `${main}<span class="sides" ${useInlineStyle ? sidesStyle : ''}>${sides}</span></div>`;
});
const resultQuadrants = { 1: null, 2: null, 3: null, 4: null };
resultQuadrants['1'] = this.sortedQuadrantsHtml(quadrants['1'], false);
resultQuadrants['2'] = this.sortedQuadrantsHtml(quadrants['2'], true);
resultQuadrants['3'] = this.sortedQuadrantsHtml(quadrants['3'], true);
resultQuadrants['4'] = this.sortedQuadrantsHtml(quadrants['4'], false);
return resultQuadrants;
},
sortedQuadrantsHtml(quadrants, ascDirection) {
const sorting = ascDirection ? 'A1B2C3D4E5678' : '8765E4D3C2B1A';
const sortedNames = _.sortBy(_.keys(quadrants), item => {
return sorting.indexOf(item);
});
const sortedQuadrant = _.map(sortedNames, name => {
return quadrants[name];
});
return sortedQuadrant.join('');
},
generateFinalHtml(quadrants, isLittleToothPrint, useInlineStyle) {
const styles = `
<style>
.tooth-chart, .little-tooth-print {
border-collapse: collapse;
}
.little-tooth-print {
display: inline-table;
}
.quadrant1 {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
text-align: right;
min-width: 50px;
}
.quadrant2 {
border-left: 1px solid #000;
border-bottom: 1px solid #000;
text-align: left;
min-width: 50px;
}
.quadrant3 {
border-left: 1px solid #000;
border-top: 1px solid #000;
text-align: left;
min-width: 50px;
}
.quadrant4 {
border-right: 1px solid #000;
border-top: 1px solid #000;
text-align: right;
min-width: 50px;
}
.sides, .supernumerary {
font-size: 12px;
position: relative;
top: -4px;
transform: scale(.9);
display: inline-block;
}
.sides {
font-size: 12px;
position: relative;
top: -4px;
transform: scale(.9);
display: inline-block;
}
</style>
`;
const html = isLittleToothPrint ? this.generateLittleToothHtml(quadrants, useInlineStyle) : this.generateFullHtml(quadrants, useInlineStyle);
if (useInlineStyle) {
return `<div style="display: inline-block">${html}</div>`;
}
return `<div style="display: inline-block">${styles}${html}</div>`;
},
generateLittleToothHtml(quadrants, useInlineStyle) {
let upper = '';
let lower = '';
if (quadrants['1'] || quadrants['2']) {
upper = `
<table class="little-tooth-print" ${useInlineStyle ? tableStyle : ''}>
<tr>
<td class="quadrant1" ${useInlineStyle ? quadrant1Style : ''}>
${quadrants['1']}
</td>
<td class="quadrant2" ${useInlineStyle ? quadrant2Style : ''}>
${quadrants['2']}
</td>
</tr>
</table>
`;
}
if (quadrants['3'] || quadrants['4']) {
lower = `
<table class="little-tooth-print" ${useInlineStyle ? tableStyle : ''}>
<tr>
<td class="quadrant4" ${useInlineStyle ? quadrant4Style : ''}>
${quadrants['4']}
</td>
<td class="quadrant3" ${useInlineStyle ? quadrant3Style : ''}>
${quadrants['3']}
</td>
</tr>
</table>
`;
}
return upper + lower;
},
generateFullHtml(quadrants, useInlineStyle) {
return `
<table class="tooth-chart" ${useInlineStyle ? tableStyle : ''}>
<tr>
<td class="quadrant1" ${useInlineStyle ? quadrant1Style : ''}>
${quadrants['1'] || '&nbsp;'}
</td>
<td class="quadrant2" ${useInlineStyle ? quadrant2Style : ''}>
${quadrants['2'] || '&nbsp;'}
</td>
</tr>
<tr>
<td class="quadrant4" ${useInlineStyle ? quadrant4Style : ''}>
${quadrants['4'] || '&nbsp;'}
</td>
<td class="quadrant3" ${useInlineStyle ? quadrant3Style : ''}>
${quadrants['3'] || '&nbsp;'}
</td>
</tr>
</table>
`;
}
},
computed: {
toothChart() {
return this.getToothSelectorHtml(this.selectedToothCodes, false, true);
}
},
mounted() {
},
};
</script>
<template>
<div
v-if="pictureDataList.length"
class="multiple-picture-view"
>
<div
:style="style"
v-for="(item, index) in pictureDataList"
:key="index"
>
<img
v-if="item"
class="img-item"
:src="item"
alt=""
/>
<span
v-if="varText"
class="var-text"
>
{{ `[${varText}]` }}
</span>
</div>
</div>
</template>
<script>
export default {
name: 'multiple-picture-view',
props: {
pictureDataList: {
type: Array,
default() {
return [];
},
},
varText: {
type: String,
default() {
return '';
}
},
width: {
type: Number,
default() {
return 300;
}
},
height: {
type: Number,
default() {
return 120;
}
},
},
computed: {
style() {
return {
position: 'relative',
width: `${this.width}px`,
height: `${this.height}px`
}
},
}
};
</script>
<style lang="less" scoped>
.multiple-picture-view {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.img-item {
width: 100%;
height: 100%;
}
.var-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 12px;
}
</style>
<template>
<!-- <a-form-item label="文字方向">
<a-radio-group v-model:value="option.writingMode">
<a-radio-button value="horizontal-tb">横向</a-radio-button>
<a-radio-button value="vertical-lr">竖向</a-radio-button>
</a-radio-group>
</a-form-item> -->
<a-form-item v-if="isShowTextType">
<a-select v-model:value="option.textType" :options="textTypeOptions" @change="changeTextType"></a-select>
</a-form-item>
<a-row :gutter="8">
<a-col :span="12">
<a-form-item>
<a-select v-model:value="option.fontFamily" :options="fontFamilyOptions"></a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item>
<a-select v-model:value="option.fontSize" :options="fontSizeOptions"></a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="8">
<a-col :span="12">
<div class="font-style-box">
<div class="item" :class="{ active: option.fontWeight !== 'normal' }" @click="onSelectFontWeight">
<visualGuiIcon mode="zitijiacu"></visualGuiIcon>
</div>
<div class="item" :class="{ active: option.fontStyle !== 'normal' }" @click="onSelectFontStyle">
<visualGuiIcon mode="zitixieti"></visualGuiIcon>
</div>
<div class="item" :class="{ active: option.textDecoration.includes('underline') }" @click="onSelectTextUnderLine">
<visualGuiIcon mode="zitixiahuaxian"></visualGuiIcon>
</div>
<div class="item" :class="{ active: option.textDecoration.includes('line-through') }" @click="onSelectTextlineThrough">
<visualGuiIcon mode="zitishanchuxian"></visualGuiIcon>
</div>
</div>
</a-col>
<a-col :span="6">
<a-form-item>
<mColorPickerVue :color="option.color" @onChange="changeColor">
<template #content>
<div class="font_color">
A
<span class="bg" :style="{ background: option.color }"></span>
</div>
</template>
</mColorPickerVue>
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item>
<mColorPickerVue :color="option.backgroundColor" @onChange="changeBgColor">
<template #content>
<div class="font_bg">
<span class="font">ab</span>
<span class="bg" :style="{ background: option.backgroundColor }"></span>
</div>
</template>
</mColorPickerVue>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="8">
<a-col :span="12">
<a-form-item>
<a-input-number class="input-number-size-ls" v-model:value="option.letterSpace" :maxlength="5"> </a-input-number>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item>
<a-input-number class="input-number-size-lh" v-model:value="option.lineHeight" :maxlength="5"> </a-input-number>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="8">
<a-col :span="24">
<a-radio-group v-model:value="option.textAlign">
<a-radio-button value="left">
<visualGuiIcon mode="zitizuoduiqi"></visualGuiIcon>
</a-radio-button>
<a-radio-button value="center">
<visualGuiIcon mode="zitijuzhong"></visualGuiIcon>
</a-radio-button>
<a-radio-button value="right">
<visualGuiIcon mode="zitiyouduiqi"></visualGuiIcon>
</a-radio-button>
<a-radio-button value="justify">
<visualGuiIcon mode="zitipingjunfenbu"></visualGuiIcon>
</a-radio-button>
</a-radio-group>
</a-col>
</a-row>
</template>
<script>
import mColorPickerVue from './m-color-picker.vue';
// 文本类型和默认样式
const textTypeOptions = [
{ label: '标题', value: '1' },
{ label: '副标题', value: '2' },
{ label: '正文', value: '3' },
];
const textTypeStyle = [
{ fontFamily: 'PingFangSC-Regular,Microsoft YaHei', fontSize: '16px', fontWeight: 'bold', color: '#000' },
{ fontFamily: 'PingFangSC-Regular,Microsoft YaHei', fontSize: '14px', fontWeight: 'bold', color: '#000' },
{ fontFamily: 'PingFangSC-Regular,Microsoft YaHei', fontSize: '12px', fontWeight: 'normal', color: '#000' },
];
const fontSizeOptions = [
{ value: '12px', label: '12px' },
{ value: '14px', label: '14px' },
{ value: '16px', label: '16px' },
{ value: '18px', label: '18px' },
{ value: '20px', label: '20px' },
{ value: '22px', label: '22px' },
{ value: '24px', label: '24px' },
{ value: '26px', label: '26px' },
];
const fontFamilyOptions = [
{ value: 'Microsoft YaHei', label: '微软雅黑' },
{ value: 'SimHei', label: '黑体' },
{ value: 'SimSong', label: '宋体' },
{ label: '仿宋', value: '仿宋' },
{ label: '楷体', value: '楷体' },
{ label: '隶书', value: '隶书' },
{ label: '幼圆', value: '幼圆' },
{ value: 'fangsong', label: 'fangsong' },
{ value: 'cursive', label: 'cursive' },
{ value: 'serif', label: 'serif' },
{ value: 'monospace', label: 'monospace' },
{ value: 'system-ui;', label: 'system-ui;' },
{ value: 'ui-serif;', label: 'ui-serif;' },
{ value: 'ui-sans-serif', label: 'ui-sans-serif' },
{ value: 'ui-monospace', label: 'ui-monospace' },
{ value: 'ui-rounded', label: 'ui-rounded' },
{ value: 'emoji', label: 'emoji' },
{ value: 'math', label: 'math' },
{ value: 'inherit', label: 'inherit' },
{ value: 'initial', label: 'initial' },
{ value: 'revert', label: 'revert' },
{ value: 'unset', label: 'unset' },
{ label: 'Andale Mono', value: "'andale mono', times" },
{ label: 'Arial', value: 'arial, helvetica, sans-serif' },
{ label: 'Arial Black', value: "'arial black', 'avant garde'" },
{ label: 'Book Antiqua', value: "'book antiqua', palatino" },
{ label: 'Comic Sans MS', value: "'comic sans ms', sans-serif" },
{ label: 'Courier New', value: "'courier new', courier" },
{ label: 'Georgia', value: 'georgia, palatino' },
{ label: 'Helvetica', value: 'helvetica' },
{ label: 'Impact', value: 'impact, chicago' },
{ label: 'Symbol', value: 'symbol' },
{ label: 'Tahoma', value: 'tahoma, arial, helvetica, sans-serif' },
{ label: 'Terminal', value: 'terminal, monaco' },
{ label: 'Times New Roman', value: "'times new roman', times" },
{ label: 'Trebuchet MS', value: "'trebuchet ms', geneva" },
{ label: 'Verdana', value: 'verdana, geneva' },
{ label: 'Webdings', value: 'Webdings' },
{ label: 'Wingdings', value: 'Wingdings' },
];
export default {
name: 'style-panel',
components: {
mColorPickerVue,
},
props: {
option: {
type: [Object],
default() {
return {};
},
},
isShowTextType: {
type: Boolean,
default() {
return true;
}
}
},
data() {
return {
textTypeOptions,
textTypeStyle,
fontFamilyOptions,
fontSizeOptions,
textTypeStyle,
};
},
methods: {
changeTextType(value) {
const { fontFamily, fontSize, fontWeight } = textTypeStyle[value - 1];
this.option.fontFamily = fontFamily;
this.option.fontSize = fontSize;
this.option.fontWeight = fontWeight;
},
onSelectFontWeight() {
this.option.fontWeight = this.option.fontWeight === 'normal' ? 'bold' : 'normal';
},
onSelectFontStyle() {
this.option.fontStyle = this.option.fontStyle === 'normal' ? 'italic' : 'normal';
},
onSelectTextUnderLine() {
this.addLine('underline');
},
addLine(type) {
const style = this.option.textDecoration;
let underline, lineThrough;
if (type === 'underline') {
underline = style.includes('underline') ? '' : 'underline';
lineThrough = style.includes('line-through') ? 'line-through' : '';
} else {
underline = style.includes('underline') ? 'underline' : '';
lineThrough = style.includes('line-through') ? '' : 'line-through';
}
this.option.textDecoration = `${underline} ${lineThrough} auto`;
console.log(this.option.textDecoration);
},
onSelectTextlineThrough() {
this.addLine('line-through');
},
changeColor(color) {
this.option.color = color;
},
changeBgColor(color) {
this.option.backgroundColor = color;
},
onSelectDelLine() {
this.option.delLine = !this.option.delLine;
},
},
};
</script>
<style lang="less" scope>
[class^='input-number'] {
position: relative;
width: 100%;
&:after {
top: 0;
right: 0;
position: absolute;
color: rgba(0, 0, 0, 0.25);
line-height: 30px;
padding-right: 4px;
}
}
.input-number-size-ls {
&:after {
content: '字距';
}
}
.input-number-size-lh {
&:after {
content: '行距';
}
}
.input-number-x {
&:after {
content: 'X';
}
}
.input-number-y {
&:after {
content: 'Y';
}
}
.input-number-width {
&:after {
content: '宽';
}
}
.input-number-height {
&:after {
content: '高';
}
}
.font-style-box {
display: flex;
height: 32px;
line-height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
.item {
flex: 1;
text-align: center;
cursor: pointer;
&.active {
color: #00a6c9;
}
}
}
.ant-radio-group {
display: flex;
width: 100%;
.ant-radio-button-wrapper {
flex: 1;
text-align: center;
}
}
.font_color {
position: relative;
line-height: 1.1;
text-align: center;
.bg {
display: block;
width: 16px;
height: 4px;
border-radius: 4px;
border: 1px solid #d9d9d9;
box-sizing: content-box;
background: #fff;
}
}
.font_bg {
position: relative;
line-height: 1.1;
text-align: center;
.font {
position: relative;
z-index: 10;
}
.bg {
display: block;
border: 1px solid #d9d9d9;
margin-top: -6px;
width: 16px;
height: 11px;
border-radius: 4px;
box-sizing: content-box;
background: #fff;
z-index: 1;
}
}
.ant-form-item {
margin-bottom: 8px;
}
</style>
<style lang="less" scoped>
.tabs-wrap {
//margin: 8px 2px 0 0;
//padding: 10px 8px;
}
</style>
<template>
<div class="tabs-wrap">
<a-tabs v-model:activeKey="contentKey">
<a-tab-pane
v-for="(item, key) in data"
:key="item.key"
:tab="item.content"
>
<slot :name="item.key" />
</a-tab-pane>
</a-tabs>
</div>
</template>
<script>
export default {
name: "tabs",
props: {
// 配置
contentKey: {
type: [String],
default() {
return "";
},
},
data: {
type: [Array],
default() {
return [];
},
},
},
};
</script>
<template>
<fields-select :formic="option.formic" :fields="fields" />
<a-form-item v-if="option.formic.content && option.formic.content.value">
<a-checkbox v-model:checked="option.formic.isShowTooth">显示牙位图</a-checkbox>
</a-form-item>
</template>
<script>
import fieldsSelect from './fields-select.vue';
export default {
name: 'tooth-optional',
components: {
fieldsSelect,
},
props: {
fields: {
type: [Array],
default() {
return [];
}
},
option: {
type: [Object],
default() {
return {};
},
},
},
computed: {
contentData() {
const data = _.forEach(this.fields, function(item) {
if (item.name) {
item.value = item.name;
}
if (item.options && item.options.length) {
item.options && item.options.forEach(option => {
option.value = option.name;
});
}
});
return data;
}
}
};
</script>
\ No newline at end of file
<template>
<div class="tooth-view">
<div v-if="isShowTooth" class="tooth-item">
<m-tooth-chart :selectedToothCodes="toothCodes"></m-tooth-chart>
</div>
<div v-if="isShowDescription" class="tooth-item text">
<template
v-for="(item, index) in descriptions"
:key="index"
>
<p
v-if="item"
class="tooth-item-text"
:style="styles"
>
{{ item }}
</p>
</template>
</div>
</div>
</template>
<script>
import mToothChart from '../components/m-tooth-chart';
export default {
name: 'tooth-view',
components: {
mToothChart,
},
props: {
toothCodes: {
type: String,
default() {
return '';
},
},
descriptions: {
type: Array,
default() {
return [];
},
},
isShowTooth: {
type: Boolean,
default() {
return true;
},
},
isShowDescription: {
type: Boolean,
default() {
return true;
},
},
styles: {
type: [Object],
default() {
return {};
},
}
},
};
</script>
<style lang="less" scoped>
.tooth-view {
display: flex;
}
.tooth-cross {
width: 100px;
height: 85px;
position: relative;
&::before {
position: absolute;
left: 0;
top: 50%;
width: 100%;
content: '';
transform: translateY(-50%);
border-top: 2px solid #212121;
}
&::after {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
height: 80%;
content: '';
border-left: 2px solid #212121;
}
}
.tooth-item + .tooth-item {
margin-left: 10px;
}
.tooth-item-text {
margin: 0;;
}
.tooth-item-text + .tooth-item-text {
margin-top: 5px;
}
.text {
flex: 1;
}
:deep(.tooth-chart) {
font-size: 10px;
}
</style>
\ No newline at end of file
<template>
<view class="fullSlider">
<div class="fake" v-if="!option.data?.length">
<font-awesome-icon icon="window-restore" class="icon"/>
</div>
<swiper
v-if="option.data?.length"
:indicator-dots="option.attr.dots"
:indicator-color = "option.attr.indicatorColor"
:autoplay="option.attr.autoplay"
:style="option.style.wrap"
:interval = "option.attr.interval"
:duration = "option.attr.duration"
:circular = "option.attr.circular"
:vertical = "option.attr.vertical"
:asing-function = "option.attr.easing"
:nums = "option.data.length"
>
<swiper-item v-for="item in option.data">
<navigator
:url="item.navigator"
:style="[{background:`url(${item.url})`}, option.style.item]"
></navigator>
</swiper-item>
</swiper>
</view>
</template>
<script>
export default {
name: "m-fullSlider",
meta: {
group: "导航组件",
title: "导航组件",
iconfont: "window-restore", // iconfont class name
label: "全屏轮播",
index: 20,
disabled: false,
visible: false
},
axes: {
x: 0,
y: 0,
w: 160,
h: 32,
},
props: {
option: {
type: [Object],
default() {
return {
style: {
wrap: {
width: "100%",
height: "calc(100vh - 5.4rem)",
},
item: {
width: "100%",
height: "calc(100vh - 5.4rem)",
backgroundPosition: "center",
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
}
},
data: [],
attr: {
dots: true,
indicatorColor: 'rgba(0, 0, 0, .3)',
autoplay: true,
interval: 5000, // 自动切换时间间隔
duration: 500, // 滑动动画时长
circular: false, // 是否采用衔接滑动
vertical: false, // 滑动方向是否为纵向
easing: 'default', // 动画效果
}
}
}
}
},
data() {
return {
slideH: 0
}
},
methods: {
handle() {
this.visible = true
}
}
}
</script>
<style lang="less" scoped>
.fullSlider {
.fake {
background-color: #ddd;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 600px;
.icon {
font-size: 3.4em;
color: rgba(0, 0, 0, 0.5);
}
}
}
</style>
<template>
<div class="imgText">
<kit-taber>
<template #content>
<kit-formic-pack :namespace="namespace" v-if="option.data.length">
<kit-formic-part key="picker" title="已选图片">
<div class="addItem" v-for="(item, index) in option.data" :key="index">
<span class="del" @click="delItemFunc(index)">删除</span>
<div class="content">
<div class="link">
<h4>链接类型:</h4>
<kit-picker-link
v-model:value="item.navigator"
:links="sourceArr.slice(1)"
@choose="onPickerLinkChoose($event, index)"
/>
</div>
<div class="img" :style="{backgroundImage:`url(${item.url})`}"></div>
</div>
<div class="customLink">
<h4>自定义参数:</h4>
<CustomLink @getCustomLink = "getCustomLink($event, index)"/>
</div>
<div class="actionLink">
<h4>活动页选择:</h4>
<ActionLink @getActionLink = "getCustomLink($event, index)"/>
</div>
</div>
</kit-formic-part>
</kit-formic-pack>
<kit-picker-context v-model:value="chosenImg" :link="iframeAddress" :limit="[1,1]" :fileCtype="'img'" :style="{display: 'block'}" v-model:visible="addVisible" @handler="iframeCallBack">
</kit-picker-context>
<a-button type="primary" class="addBtn" @click="addNewFunc">
<template #icon><PlusOutlined /></template>
添加图片
</a-button>
</template>
<template #style>
<kit-formic-pack :namespace="namespace">
<kit-formic-part key="sliderSetting" title="内容设置">
<kit-formic-item>
<a-row type="flex" justify="space-between" align="middle" style="margin-bottom: 15px">
<a-col :span="12">
是否显示面板指示点
</a-col>
<a-col :span="12">
<a-radio-group v-model:value="option.attr.dots" button-style="solid">
<a-radio-button :value="true"></a-radio-button>
<a-radio-button :value="false"></a-radio-button>
</a-radio-group>
</a-col>
</a-row>
</kit-formic-item>
<kit-formic-item>
<a-row type="flex" justify="space-between" align="middle" style="margin-bottom: 15px">
<a-col :span="12">
是否自动切换
</a-col>
<a-col :span="12">
<a-radio-group v-model:value="option.attr.autoplay" button-style="solid">
<a-radio-button :value="true"></a-radio-button>
<a-radio-button :value="false"></a-radio-button>
</a-radio-group>
</a-col>
</a-row>
</kit-formic-item>
<kit-formic-item>
<a-row type="flex" justify="space-between" align="middle" style="margin-bottom: 15px">
<a-col :span="12">
自动切换时间间隔
</a-col>
<a-col :span="12">
<a-radio-group v-model:value="option.attr.interval" button-style="solid">
<a-radio-button :value="3000">3s</a-radio-button>
<a-radio-button :value="4000">4s</a-radio-button>
<a-radio-button :value="5000">5s</a-radio-button>
</a-radio-group>
</a-col>
</a-row>
</kit-formic-item>
<kit-formic-item>
<a-row type="flex" justify="space-between" align="middle" style="margin-bottom: 15px">
<a-col :span="9">
滑动动画时长
</a-col>
<a-col :span="15">
<a-radio-group v-model:value="option.attr.duration" button-style="solid">
<a-radio-button :value="400">0.4s</a-radio-button>
<a-radio-button :value="500">0.5s</a-radio-button>
<a-radio-button :value="600">0.6s</a-radio-button>
</a-radio-group>
</a-col>
</a-row>
</kit-formic-item>
<kit-formic-item>
<a-row type="flex" justify="space-between" align="middle" style="margin-bottom: 15px">
<a-col :span="12">
是否采用衔接滑动
</a-col>
<a-col :span="12">
<a-radio-group v-model:value="option.attr.circular" button-style="solid">
<a-radio-button :value="true"></a-radio-button>
<a-radio-button :value="false"></a-radio-button>
</a-radio-group>
</a-col>
</a-row>
</kit-formic-item>
<kit-formic-item>
<a-row type="flex" justify="space-between" align="middle" style="margin-bottom: 15px">
<a-col :span="12">
滑动方向是否为纵向
</a-col>
<a-col :span="12">
<a-radio-group v-model:value="option.attr.vertical" button-style="solid">
<a-radio-button :value="true"></a-radio-button>
<a-radio-button :value="false"></a-radio-button>
</a-radio-group>
</a-col>
</a-row>
</kit-formic-item>
<kit-formic-item>
<a-row type="flex" justify="space-between" align="middle" style="margin-bottom: 15px">
<a-col :span="12">
动画效果
</a-col>
<a-col :span="12">
<a-select
ref="select"
v-model:value="option.attr.easing"
>
<a-select-option :value="'default'">默认缓动函数</a-select-option>
<a-select-option :value="'linear'">线性动画</a-select-option>
<a-select-option :value="'easeInCubic'">缓入动画</a-select-option>
<a-select-option :value="'easeOutCubic'">缓出动画</a-select-option>
<a-select-option :value="'easeInOutCubic'">缓入缓出动画</a-select-option>
</a-select>
</a-col>
</a-row>
</kit-formic-item>
</kit-formic-part>
</kit-formic-pack>
</template>
</kit-taber>
</div>
</template>
<script>
import {GOODS_LIST, GOODS_TYPE, GOODS_DETAIL, GOODS_LINK, GOODS_TYPE_LINK, GOODS_PIC_LINK} from "../common/constent";
import CustomLink from "../components/m-customLink";
import ActionLink from "../components/m-actionLink";
export default {
name: "m-fullSlider-optional",
props: {
option: {}
},
components: {
CustomLink,
ActionLink
},
data() {
return {
namespace: ["picker", "sliderSetting"],
resultIndex: null,
chosenImg: [],
iframeType: null,
iframeAddress: '',
addVisible: false,
sourceArr: [
{
label: "关联图片",
value: "",
iframeLink: GOODS_PIC_LINK,
type: 0
},
{
label: "关联商品分类",
value: GOODS_TYPE,
iframeLink: "",
type: 1
},
{
label: "关联商品列表",
value: GOODS_LIST,
iframeLink: GOODS_TYPE_LINK,
type: 2
},
{
label: "关联商品详情",
value: GOODS_DETAIL,
iframeLink: GOODS_LINK,
type: 3
}
]
}
},
methods: {
delItemFunc(index) {
this.option.data.splice(index,1)
},
onPickerLinkChoose(data, index) {
this.iframeAddress = this.sourceArr[data.type].iframeLink;
this.iframeType = data.type
this.resultIndex = index
if(data.iframeLink) {
this.addVisible = true;
}else {
this.option.data[this.resultIndex].navigator = data.value
}
},
iframeCallBack(val) {
switch (this.iframeType) {
case 0:
this.option.data.push({
url: val[0].fileUrl,
navigator: "",
})
break;
case 2:
this.option.data[this.resultIndex].navigator = this.sourceArr[this.iframeType].value+'?classtreeType=home&classtreeCode='+val[0].classtreeCode
break;
case 3:
this.option.data[this.resultIndex].navigator = this.sourceArr[this.iframeType].value+'?skuCode='+val[0].skuCode
break;
default:
return;
}
},
addNewFunc() {
this.iframeAddress = GOODS_PIC_LINK;
this.addVisible = true;
this.iframeType = 0;
},
getCustomLink(val, index) {
this.option.data[index].navigator = val
}
}
}
</script>
<style scoped lang="less">
.addItem {
width: 295px;
background: rgba(240,242,245, .7);
margin: 0 auto 20px;
position: relative;
padding: 40px 10px 10px;
box-sizing: border-box;
border-radius: 3px;
.del {
position: absolute;
cursor: pointer;
right: 10px;
top: 10px;
font-size: 12px;
color: #aaa;
}
.content {
width: 100%;
display: flex;
justify-content: space-between;
.link {
width: 180px;
p {
word-wrap: break-word;
word-break: break-all;
}
}
.img {
width: 70px;
height: 70px;
background-color: #ddd;
background-position: center;
background-repeat: no-repeat;
background-size: contain;
}
}
.customLink {
margin-bottom: 20px;
}
}
.addBtn {
width: 295px;
margin: 0 auto;
display: block;
}
//.pack-skin .ant-form-item {
// margin-bottom: 35px;
//}
//.ant-col {
// p {
// margin: 0;
// height: 32px;
// line-height: 32px;
// }
//}
</style>
<template>
<div class="goodsListBox" :style="goodsListBoxStyle">
<div class="goodsList" v-if="option.data.length > 0">
<div class="goodsList-item" :style="{borderRadius: style.borderRadius}" v-for="(item, index) in option.data" :key="index">
<navigator class="navigatoBox" :url="`/pages/goodsDetail/main?skuCode=${item.skuCode}`">
<div class="goodsList-item-img">
<div class="goodsList-item-img-item" :style="{backgroundImage: 'url('+ item.dataPic +')'}"></div>
<!-- <img class="goodsList-item-img-item" :src="item.dataPic ? item.dataPic : ''" :onerror="defaultImg" alt=""> -->
</div>
<div class="goodsList-item-info">
<div class="goodsList-item-info-title" :style="goodsNameStyle">{{item.goodsName}}</div>
<div class="goodsList-item-info-price">
<!-- 普通价 -->
<div :style="goodsPriceStyle" v-if="!option.memberPrice">{{item.pricesetNprice}}</div>
<!-- 营销价 -->
<div v-else class="member">
<div class="member-Sty">
<div class="member-Sty-price" :style="goodsPriceStyle">
<span class="member-Sty-price-icon"></span>
<span>{{item.pricesetNprice}}</span>
</div>
<div class="member-Sty-label">会员价</div>
</div>
<div class="scribingPrice">
<span class="scribingPrice-icon"></span>
<span>{{item.pricesetMakeprice}}</span>
</div>
</div>
</div>
<!-- 购物车 -->
<div class="goodsList-item-info-car" v-if="option.shopCar"></div>
</div>
</navigator>
</div>
</div>
<div class="goodsList" v-else>
<div :class="goodsItemClass" :style="{borderRadius: style.borderRadius}" v-for="(item, index) in option.default" :key="index">
<div class="goodsList-item-img">
<!-- <img class="goodsList-item-img-item" :src="item.dataPic" alt=""> -->
<div class="goodsList-item-img-item" :style="{backgroundImage: 'url('+item.dataPic+')'}"></div>
</div>
<div class="goodsList-item-info" :style="goodsItemInfoSty">
<div class="goodsList-item-info-title" :style="goodsNameStyle">{{item.goodsName}}</div>
<div class="goodsList-item-info-price">
<div :style="goodsPriceStyle" v-if="!option.memberPrice">{{item.pricesetNprice}}</div>
<div v-else class="member">
<div class="member-Sty">
<div class="member-Sty-price" :style="goodsPriceStyle">
<span class="member-Sty-price-icon"></span>
<span>{{item.pricesetNprice}}</span>
</div>
<div class="member-Sty-label">会员价</div>
</div>
<div class="scribingPrice">
<span class="scribingPrice-icon"></span>
<span>{{item.pricesetMakeprice}}</span>
</div>
</div>
</div>
<!-- 购物车 -->
<div class="goodsList-item-info-car" v-if="option.shopCar"></div>
</div>
</div>
</div>
</div>
</template>
<script>
import default_img from "../common/img/placeholder_img.png"
export default {
name: "m-goodsList",
meta: {
group: "商品列表",
title: "商品列表",
iconfont: "list-check", // iconfont class name
label: "商品列表",
index: 20,
disabled: false,
},
axes: {
x: 0,
y: 0,
w: 160,
h: 32,
},
computed: {
goodsNameStyle() {
let style = { color: this.option.css.color };
return style;
},
goodsListBoxStyle(){
const {
color,
priceFontColor,
backgroundColor,
paddingTop,
paddingBottom
} = this.option.css;
return {
color,
priceFontColor,
backgroundColor,
paddingTop,
paddingBottom
}
},
style(){
const { borderRadius } = this.option.css;
return { borderRadius }
},
goodsPriceStyle() {
let style = { color: this.option.css.priceFontColor };
return style;
},
goodsItemClass() {
let Class = this.option.style?.boxType == 'cards' ? 'goodsList-item itemCards' : this.option.style?.boxType == 'stroke' ? 'goodsList-item itemStroke' : 'goodsList-item';
return Class;
},
goodsItemInfoSty() {
if(this.option.style?.boxType == 'stroke'){
let style = { borderTop: '1px solid rgba(211, 211, 211, .4)'};
return style;
}
},
},
data() {
return {
defaultImg: `this.src="${default_img}"` //默认图地址
}
},
props: {
// 配置
option: {
type: [Object],
default() {
return {
css: {
color: "#000000",
priceFontColor: "#b79f77",
backgroundColor: "#FFFFFF",
paddingTop: '10px',
paddingBottom: '10px',
borderRadius: '0', //边角样式
},
style: {
boxType: 'flat', // 商品样式
},
shopCar: false,
memberPrice: false,
default: [
{ dataPic: default_img, pricesetNprice: "6000", pricesetMakeprice: "4200", goodsName:"藏蓝色连衣裙藏蓝色连衣裙藏蓝色连衣裙", url: "默认链接" },
{ dataPic: default_img, pricesetNprice: "9999", pricesetMakeprice: "99", goodsName:"卡其色风衣", url: "默认链接" },
],
data: [],
};
},
},
source: {
type: [Object],
default() {
return {};
},
},
},
};
</script>
<style lang="less" scoped>
.goodsListBox {
padding: 0 10px;
.goodsList{
display: flex;
justify-content: space-between;
flex-wrap: wrap;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
&-item {
width: 48%;
margin-bottom: 10px;
background: #fff;
display: flex;
flex-flow: column nowrap;
overflow: hidden;
.navigatoBox {
height: 100%;
}
&-img {
width: 100%;
&-item {
padding-top: 100%;
background-position: 50%;
background-repeat: no-repeat;
background-size: 100% 100%;
width: 100%;
// width: 100%;
// height: 100%;
}
}
&-info {
padding: 0.5rem;
display: flex;
flex-flow: column nowrap;
justify-content: space-between;
flex-grow: 1;
position: relative;
&-title {
font-weight: 500;
width: 100%;
color: #000;
font-size: 1.5rem;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
}
&-price {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
font-size: 1.7rem;
margin-top: 15px;
line-height: 1;
.member {
width: 100%;
height: 100%;
&-Sty {
display: flex;
align-items: center;
&-price {
font-size: 1.7rem;
margin-right: 5px;
line-height: 1;
span {
font-family: font-wemo;
}
&-icon {
font-size: 12px;
}
}
&-label {
padding: 3px;
font-size: 1rem;
display: flex;
align-items: center;
background-color: #b79f77;
border-radius: 3px;
}
}
.scribingPrice {
color: #ccc;
font-size: 0.9rem;
text-decoration: line-through;
&-icon {
font-size: 12px;
}
}
}
}
&-car {
font-weight: 700;
background: #b79f77;
width: 1.4rem;
height: 1.4rem;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0.7rem;
font-size: 1.4rem;
position: absolute;
right: 10px;
bottom: 10px;
}
}
}
.itemCards {
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
}
.itemStroke {
border: 1px solid rgba(211, 211, 211, .4);
}
}
}
</style>
<template>
<div class="navigator-wrap">
<kit-taber>
<template #content>
<kit-formic-pack :namespace="namespace">
<kit-formic-part key="goods" title="商品选择">
<kit-formic-item>
<kit-picker-context v-model:value="option.data" :link="fromicLink" :limit="[1,5]" :fileCtype="'img'">
<template v-slot="{ event }">
<a-button type="primary" @click="event.open">请选择商品</a-button>
</template>
</kit-picker-context>
</kit-formic-item>
</kit-formic-part>
<!-- 加入购物车功能 -->
<kit-formic-part
class="option"
key="content"
title="商品内容"
>
<!-- <div class="goodsContent">
<div>
<span class="shopCar">购物车</span>
<span class="checkedText">{{option.shopCar ? "显示" : "不显示"}}</span>
</div>
<a-switch v-model:checked="option.shopCar"/>
</div> -->
<div class="goodsContent">
<div>
<span class="shopCar">划线价</span>
<span class="checkedText">{{option.memberPrice ? "显示" : "不显示"}}</span>
</div>
<a-switch v-model:checked="option.memberPrice"/>
</div>
<div class="selectedGoods">
<div>已选商品(可拖拽切换顺序):</div>
<draggable
v-if="option.data"
v-model="option.data"
@start="drag=true"
@end="drag=false"
item-key="goodsId"
class="selectedItems">
<template #item="{element}">
<div class="goodsSelect">
<div class="goodsSelect-img">
<img :src="element.dataPic ? element.dataPic : ''" :onerror="defaultImg">
</div>
<div class="goodsSelect-info">
<div class="goodsSelect-info-name">
{{ element.goodsName }}
</div>
<div class="goodsSelect-info-price">
<div>{{element.pricesetNprice}}</div>
</div>
</div>
</div>
</template>
</draggable>
</div>
</kit-formic-part>
</kit-formic-pack>
</template>
<template #style>
<div class="wrap--style">
<kit-formic-pack :namespace="stylespace">
<!-- Part as Form -->
<kit-formic-part key="contentStyle" title="内容样式">
<div class="goodsContent">
<span>商品样式</span>
<div>
<a-col :span="24">
<a-radio-group v-model:value="option.style.boxType" button-style="solid">
<a-radio-button value="flat">扁平</a-radio-button>
<a-radio-button value="cards">卡牌</a-radio-button>
<a-radio-button value="stroke">描边</a-radio-button>
</a-radio-group>
</a-col>
</div>
</div>
<div class="goodsContent">
<span>边角样式</span>
<div>
<a-col :span="24">
<a-radio-group v-model:value="option.css.borderRadius" button-style="solid">
<a-radio-button value="0">直角</a-radio-button>
<a-radio-button value="6px">圆角</a-radio-button>
<a-radio-button value="10px">大圆角</a-radio-button>
</a-radio-group>
</a-col>
</div>
</div>
</kit-formic-part>
<kit-formic-part key="assemblyStyle" title="组件样式">
<div class="goodsContent">
<div>
<span class="shopCar">上边距</span>
<span class="checkedText">{{option.css.paddingTop}}</span>
</div>
<a-col :span="12">
<a-radio-group v-model:value="option.css.paddingTop" button-style="solid">
<a-radio-button value="0"></a-radio-button>
<a-radio-button value="10px"></a-radio-button>
<a-radio-button value="15px"></a-radio-button>
</a-radio-group>
</a-col>
</div>
<div class="goodsContent">
<div>
<span class="shopCar">下边距</span>
<span class="checkedText">{{option.css.paddingBottom}}</span>
</div>
<a-col :span="12">
<a-radio-group v-model:value="option.css.paddingBottom" button-style="solid">
<a-radio-button value="0"></a-radio-button>
<a-radio-button value="10px"></a-radio-button>
<a-radio-button value="15px"></a-radio-button>
</a-radio-group>
</a-col>
</div>
<div class="optionCol">
<span>背景颜色</span>
<div>
<kit-picker-color
@setColor="onPickerColorBg"
:reset="true"
:value="true"
:dColor="option.css.backgroundColor"
/>
</div>
</div>
</kit-formic-part>
</kit-formic-pack>
</div>
</template>
</kit-taber>
</div>
</template>
<script>
import draggable from 'vuedraggable'
import default_img from "../common/img/placeholder_img.png"
import { GOODS_LINK } from "../common/constent.js"
export default {
name: "m-goodsList-optional",
components: {
draggable
},
data() {
return {
namespace: ["goods", "content"],
stylespace: ["contentStyle", 'assemblyStyle'],
drag: false,
defaultImg: `this.src="${default_img}"`, //默认图地址
fromicLink: GOODS_LINK,
};
},
watch: {
},
methods: {
onPickerColorTitle(color) {
this.option.css.titleFontColor = color
},
onPickerColorPrice(color) {
this.option.css.priceFontColor = color
},
onPickerColorBg(color) {
this.option.css.backgroundColor = color
}
},
props: {
// 配置
option: {
type: [Object],
default() {
return {};
},
},
source: {
type: [Object],
default() {
return {};
},
},
},
};
</script>
<style lang="less" scoped>
.image {
display: block;
width: 100%;
}
.navigator-wrap {
.optionCol {
display: flex;
justify-content: space-between;
span {
line-height: 30px;
}
}
.goodsContent {
display: flex;
justify-content: space-between;
align-items: center;
margin: 15px 0;
.shopCar {
margin-right: 20px;
}
.checkedText{
color: #9797a1;
}
}
.selectedGoods {
margin: 10px 0;
.selectedItems {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
.goodsSelect {
width: 45%;
height: 180px;
margin-bottom: 10px;
&-img {
width: 100%;
height: 120px;
img {
width: 100%;
height: 100%;
}
}
&-info {
width: 100%;
height: 60px;
padding: 5px;
display: flex;
flex-direction: column;
justify-content: space-between;
&-name {
font-size: 14px;
font-weight: 700;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
}
&-price {
font-size: 14px;
}
}
}
}
}
.ant-radio-group .ant-radio-button-wrapper{
flex: none;
}
}
</style>
<style lang="less" scoped>
.picture {
&-navigator {
&-img {
display: block;
width: 100%;
}
}
.fake {
background-color: #ddd;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 200px;
.icon {
font-size: 3.4em;
color: rgba(0, 0, 0, 0.5);
}
}
}
</style>
<template>
<div class="picture">
<div class="fake" v-if="!option.picture?.img">
<font-awesome-icon icon="image" class="icon"/>
</div>
<navigator v-else class="picture-navigator" :open-type="openType" :url="option.picture.url">
<img class="picture-navigator-img" :style="option.css" :src="option.picture.img" mode="widthFix" @click="jumpToPage" />
</navigator>
</div>
</template>
<script>
import { GOODS_TYPE } from '../common/constent.js';
export default {
name: 'm-img',
meta: {
group: '图片组件',
title: '图片组件',
iconfont: 'image', // iconfont class name
label: '图片',
index: 20,
disabled: false,
},
axes: {
x: 0,
y: 0,
w: 160,
h: 32,
},
data() {
return {
openType: 'navigate',
};
},
props: {
option: {
type: [Object],
default() {
return {
picture: {
img: '',
name: '',
url: '',
},
isShowPic: false,
css: {
borderRadius: '0px',
paddingTop: '0px',
paddingBottom: '0px',
paddingLeft: '0px',
paddingRight: '0px',
},
};
},
},
},
mounted() {
// this.option.picture.url === GOODS_TYPE ? (this.openType = 'switchTab') : (this.openType = 'navigate');
},
};
</script>
<style lang="less" scoped>
.context {
& {
width: 220px;
display: flex;
justify-content: space-between;
}
&-picture {
& {
height: 70px;
width: 70px;
position: relative;
}
&:hover &-mask {
opacity: 1;
}
&-img {
display: block;
height: 100%;
width: 100%;
border-radius: 2px;
}
&-mask {
background: rgba(101, 101, 101, 0.6);
color: #ffffff;
opacity: 0;
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
&-text {
background-color: #ffffff;
color: #000000;
padding: 4px 8px;
border-radius: 2px;
}
}
}
&-delBtn {
height: 20px;
line-height: 20px;
text-align: center;
font-size: 10px;
color: #aba9a9;
cursor: pointer;
}
}
.link-radio-group {
& {
flex-direction: column;
}
& > label {
width: 100%;
border: none;
border-radius: 4px !important;
text-align: left;
&:before,
&:after {
content: none !important;
}
}
}
.styles {
color: #595961;
font-size: 12px;
}
</style>
<template>
<div class="imgText">
<kit-taber>
<template #content>
<kit-formic-pack :namespace="namespace">
<kit-formic-part key="picker" title="图片设置">
<kit-formic-item title="图片选择">
<kit-picker-context v-model:value="dataSource.context" v-model:visible="addVisible" :link="link" :limit="[1, 1]" :fileCtype="'img'" @handler="onContextHandler">
<template v-slot="{ event }">
<div class="context">
<a-button @click="openPic" v-show="!option.isShowPic">
选择图片
<template #icon><picture-outlined /></template>
</a-button>
<div class="context-picture" v-show="option.isShowPic">
<img class="context-picture-img" :src="option.picture.img" mode="widthFix" />
<div class="context-picture-mask">
<text class="context-picture-mask-text" @click="openPic">替换</text>
</div>
</div>
<text class="context-delBtn" v-show="option.isShowPic" @click="delPicture">删除</text>
</div>
</template>
</kit-picker-context>
</kit-formic-item>
<kit-formic-item title="跳转设置" v-show="option.isShowPic">
<kit-picker-link v-model:value="option.picture.url" :links="linkOptions" @choose="onPickerLinkChange" />
</kit-formic-item>
<kit-formic-item title="自定链接" v-show="option.isShowPic">
<CustomLink style="width: 98%;" @getCustomLink = "getCustomLink($event, index)"/>
</kit-formic-item>
<kit-formic-item title="自定链接" v-show="option.isShowPic">
<ActionLink @getActionLink = "getCustomLink($event, index)"/>
</kit-formic-item>
</kit-formic-part>
</kit-formic-pack>
<a-modal v-model:visible="visible" ok-text="确认" cancel-text="取消" title="内容选择" @ok="success">
<a-radio-group class="link-radio-group" v-model:value="link" button-style="solid">
<a-radio-button v-for="option in links" :value="option.value"> {{ option.label }} </a-radio-button>
</a-radio-group>
</a-modal>
</template>
<template #style>
<div class="styles">
<a-form>
<kit-formic-pack :namespace="namespace">
<kit-formic-part :style="{marginBottom: '30px'}" key="content" title="内容样式">
<a-row type="flex" justify="space-around" align="middle">
<a-col :span="8"> 边角样式 </a-col>
<a-col :span="16">
<a-radio-group v-model:value="option.css.borderRadius" button-style="solid">
<a-radio-button value="0px">直角</a-radio-button>
<a-radio-button value="5px">圆角</a-radio-button>
<a-radio-button value="10px">大圆角</a-radio-button>
</a-radio-group>
</a-col>
</a-row>
</kit-formic-part>
<kit-formic-part key="compontent" title="组件样式">
<m-component-style
v-model:topMargins="option.css.paddingTop"
v-model:bottomMargins="option.css.paddingBottom"
:isShowPx="false"
></m-component-style>
</kit-formic-part>
</kit-formic-pack>
</a-form>
</div>
</template>
</kit-taber>
</div>
</template>
<script>
import { GOODS_LIST, GOODS_TYPE, GOODS_DETAIL, GOODS_PIC_LINK, GOODS_TYPE_LINK, GOODS_LINK } from '@/common/constent.js';
import mComponentStyle from '../components/m-component-style.vue';
import CustomLink from "../components/m-customLink";
import ActionLink from "../components/m-actionLink";
export default {
components: {
mComponentStyle,
CustomLink,
ActionLink
},
name: 'm-img-optional',
data() {
return {
namespace: ['picker', 'content', 'compontent'],
dataSource: {
// 选择链接数据
link: 'Default Link',
// 选择内容数据
context: [],
nickname: '',
sex: true,
agreements: false,
color: '#000',
},
linkOptions: [
{
label: '商品详情',
value: GOODS_DETAIL,
},
{
label: '商品列表',
value: GOODS_LIST,
},
{
label: '商品分类',
value: GOODS_TYPE,
},
],
links: [
{
label: '商品分类',
value: GOODS_TYPE_LINK,
},
],
link: GOODS_PIC_LINK,
visible: false,
addVisible: false,
};
},
methods: {
getCustomLink(val) {
this.option.picture.url = val
},
//选择跳转内容 modal 事件 start
open() {
this.visible = true;
},
close() {
this.visible = false;
},
success(event) {
//saas modal
this.addVisible = true;
// Close
this.close();
},
//选择跳转内容 modal 事件 end
//图片 SaasModal open
openPic() {
this.link = GOODS_PIC_LINK;
this.addVisible = true;
},
//链接回调
onPickerLinkChange({ label, value }) {
this.option.picture.url = value;
switch (value) {
case GOODS_LIST:
this.open();
break;
case GOODS_DETAIL:
this.link = GOODS_LINK;
this.addVisible = true;
break;
default:
break;
}
},
//清除已选图片
delPicture() {
this.option.picture.img = '';
this.option.picture.name = '';
this.option.picture.url = '';
this.option.isShowPic = false;
},
//iframe Saas 回调
onContextHandler(val) {
switch (this.link) {
case GOODS_PIC_LINK:
this.option.picture.img = val[0].fileUrl;
this.option.picture.name = val[0].fileName;
this.option.isShowPic = true;
break;
case GOODS_LINK:
this.option.picture.url = `${this.option.picture.url}?skuCode=${val[0].skuCode}`;
break;
default:
this.option.picture.url = `${this.option.picture.url}?classtreeType=home&classtreeCode=${val[0].classtreeCode}`;
break;
}
},
},
};
</script>
<style lang="less" scoped>
.sample {
padding: 120px 60px;
}
</style>
<template>
<div class="sample">
{{ option.text }}
</div>
<swiper class="swiper">
<swiper-item>
<view class="swiper-item uni-bg-red">A</view>
</swiper-item>
<swiper-item>
<view class="swiper-item uni-bg-green">B</view>
</swiper-item>
<swiper-item>
<view class="swiper-item uni-bg-blue">C</view>
</swiper-item>
</swiper>
</template>
<script>
export default {
name: "m-sample",
meta: {
group: "示例组件",
title: "示例组件",
iconfont: "vial", // iconfont class name
label: "示例",
index: 7,
disabled: false,
},
axes: {
x: 0,
y: 0,
w: 180,
h: 60,
},
props: {
// 配置
option: {
type: [Object],
default() {
return {
text: "示例2132组件",
css: {},
preset: {},
formic: {},
vars: {},
};
},
},
source: {
type: [Object],
default() {
return {};
},
},
vars: {
type: [Object],
default() {
return {};
},
},
},
data() {
return {};
},
};
</script>
<style lang="less" scoped>
/* Any Style Writing */
</style>
<template>
<!-- Classify Taber -->
<kit-taber>
<!-- Content Setting -->
<template #content>
<!-- One Pack -->
<kit-formic-pack :namespace="namespace">
<kit-formic-part key="customLink" title="选择自定义页面">
<kit-formic-item>
<CustomLink @getCustomLink = "getCustomLink"/>
</kit-formic-item>
</kit-formic-part>
<!-- Part as Form -->
<kit-formic-part key="picker" title="Some Picker">
<!-- Item: Link -->
<kit-formic-item title="链接选择">
<kit-picker-link
v-model:value="dataSource.link"
:links="linkOptions"
@choose="onPickerLinkChoose"
/>
</kit-formic-item>
<!-- Item: Colour -->
<kit-formic-item title="颜色选择">
<kit-picker-color
@setColor="onPickerColorChoose"
:reset="true"
:value="true"
/>
</kit-formic-item>
<button @click="() => this.visible = true">点我</button>
{{ visible }}
<!-- Item: Context -->
<kit-formic-item title="内容选择">
<kit-picker-context v-model:value="dataSource.context" link="http://localhost:8080/frame-saas" :limit="[1,5]" @handler="onContextHandler" v-model:visible="visible">
<template v-slot="{ event }">
<a-button type="primary" @click="event.open">请选择内容</a-button>
<pre>
{{ dataSource.context }}
</pre>
</template>
</kit-picker-context>
</kit-formic-item>
</kit-formic-part>
<!-- Part as Form -->
<kit-formic-part key="personal" title="个人信息">
<!-- Item -->
<kit-formic-item title="昵称设置">
<a-input v-model:value="dataSource.nickname" placeholder="请输入" />
</kit-formic-item>
<!-- Item -->
<kit-formic-item title="性别">
<a-switch
checked-children="男"
un-checked-children="女"
v-model:checked="dataSource.sex"
/>
</kit-formic-item>
</kit-formic-part>
<!-- Part as Form -->
<kit-formic-part key="agreements" title="协议">
<kit-formic-item>
<a-checkbox v-model:checked="dataSource.agreements">
辅助线
</a-checkbox>
</kit-formic-item>
</kit-formic-part>
</kit-formic-pack>
</template>
<!-- Style Setting -->
<template #style> 456 </template>
</kit-taber>
</template>
<script>
import CustomLink from "../components/m-customLink.vue"
export default {
name: "m-sample-optional",
data() {
return {
// Namespace for Park Opener
namespace: ["picker", "personal", "agreements", "picker-color", "customLink"],
// Link Options
linkOptions: [
{
label: "商品详情",
value: "https://www.baidu.com/",
},
{
label: "活动列表",
value: "https://www.google.com/",
},
{
label: "自定义页面",
value: "/pages/home/main",
},
],
dataSource: {
// 选择链接数据
link: "Default Link",
// 选择内容数据
context: [],
nickname: "",
sex: true,
agreements: false,
color: "#000",
value: ""
},
rules: {
nickname: [
{
required: true,
trigger: "blur",
message: `Please input nickname ...`,
},
],
sex: [{ required: true }],
},
visible: false,
};
},
components: {
CustomLink
},
methods: {
// 链接选择
onPickerLinkChoose({ label, value }) {
console.log('链接选择', label, value);
},
// 颜色选择
onPickerColorChoose(color) {
console.log("颜色选择:", color);
},
// 内容选择
onContextChoose({context}) {
console.log('内容选择:', context);
},
// 内容选择 Emit 回调
onContextHandler(value) {
console.log('onContextHandler: ', value);
},
getCustomLink(val) {
console.log(val)
}
},
};
</script>
<template>
<view class="m-video" :style="option.style">
<font-awesome-icon v-if="!option.attr.url" icon="circle-play" class="icon"/>
<video
v-if="option.attr.url"
:src="option.attr.url"
:poster="option.attr.cover"
:loop="option.attr.loop"
:autoplay="option.attr.autoplay"
:style="{
width: option.style.width,
height: option.style.height
}"
/>
</view>
</template>
<script>
export default {
name: "m-video",
meta: {
group: "视频组件",
title: "视频",
iconfont: "clapperboard", // iconfont class name
label: "视频",
index: 1,
disabled: false,
visible: false
},
axes: {
x: 0,
y: 0,
w: 160,
h: 32,
},
props: {
option: {
type: [Object],
default() {
return {
style: {
width: "100%",
minHeight: "240px",
marginTop: "0px",
marginBottom: "0px"
},
attr: {
cover: "",
url: "",
autoplay: false,
loop: false
}
}
}
}
},
created() {
// this.option.style.width = this.setting?.size.w / 16 + 'rem'
// this.option.style.height = this.setting?.size.w * 9 / 16 / 16 + 'rem'
}
}
</script>
<style lang="less" scoped>
.m-video {
background-color: #ddd;
display: flex;
justify-content: center;
align-items: center;
.icon {
font-size: 3.4em;
color: rgba(0, 0, 0, 0.5);
}
}
</style>
<template>
<kit-taber>
<template #content>
<kit-formic-pack :namespace="namespace">
<kit-formic-part key="set" title="视频设置">
<a-row type="flex" justify="space-around" align="middle" style="margin-bottom: 30px">
<a-col :span="8" style="margin-left: 12px"> 视频选择: </a-col>
<a-col :span="15" style="text-align: right">
<a-button @click="chooseSource('video')">选择视频</a-button>
</a-col>
</a-row>
<a-row type="flex" justify="space-around" align="middle">
<a-col :span="8" style="margin-left: 12px"> 封面选择: </a-col>
<a-col :span="15" style="text-align: right">
<a-button @click="chooseSource('img')">选择图片</a-button>
</a-col>
</a-row>
</kit-formic-part>
</kit-formic-pack>
</template>
<template #style>
<kit-formic-pack :namespace="namespace">
<kit-formic-part key="set" title="内容样式" style="margin-bottom: 30px">
<a-row type="flex" justify="space-between" align="middle" style="margin-bottom: 15px">
<a-col :span="8" push="1"> 自动播放: </a-col>
<a-col :span="8" style="text-align: right">
<a-radio-group v-model:value="option.attr.autoplay" button-style="solid">
<a-radio-button :value="true"></a-radio-button>
<a-radio-button :value="false"></a-radio-button>
</a-radio-group>
</a-col>
</a-row>
<a-row type="flex" justify="space-between" align="middle">
<a-col :span="8" push="1"> 循环播放: </a-col>
<a-col :span="8" style="text-align: right">
<a-radio-group v-model:value="option.attr.loop" button-style="solid">
<a-radio-button :value="true"></a-radio-button>
<a-radio-button :value="false"></a-radio-button>
</a-radio-group>
</a-col>
</a-row>
</kit-formic-part>
</kit-formic-pack>
<kit-formic-pack :namespace="namespace">
<kit-formic-part key="set" title="组件样式">
<m-component-style
v-model:topMargins="option.style.marginTop"
v-model:bottomMargins="option.style.marginBottom"
:isShowPx="false"
></m-component-style>
</kit-formic-part>
</kit-formic-pack>
</template>
</kit-taber>
<kit-picker-context
v-model:value="sourceArr"
:link="GOODS_PIC_LINK"
:limit="[1,1]"
:fileCtype="type"
:style="{display: 'block'}"
v-model:visible="modalVisible"
@handler="iframeCallBack"
/>
</template>
<script>
import mComponentStyle from '../components/m-component-style.vue';
import {GOODS_PIC_LINK} from "../common/constent";
export default {
name: "m-video-optional",
props: {
option: {}
},
components: {
mComponentStyle,
},
data() {
return {
namespace: ["set"],
GOODS_PIC_LINK,
modalVisible: false,
sourceArr: [],
type: ''
}
},
methods: {
chooseSource(type) {
this.modalVisible = true;
this.type = type;
},
iframeCallBack(val) {
let result = val[0].fileUrl;
switch (this.type) {
case "img":
this.option.attr.cover = result
break;
case "video":
this.option.attr.url = result
break;
default:
return false;
}
console.log(this.option)
}
}
}
</script>
let { version } = require('./package.json');
const { original } = JSON.parse(process.env.npm_config_argv) || {};
version = original.filter((item) => /latest/.test(item)).shift() || version;
module.exports = {
outputDir: `dist/${version}`,
configureWebpack: {
output: {
libraryExport: 'default',
},
},
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment