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>
This diff is collapsed.
<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
This diff is collapsed.
This diff is collapsed.
<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>
This diff is collapsed.
<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>
This diff is collapsed.
<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>
This diff is collapsed.
<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>
This diff is collapsed.
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