feat(商品sku)
This commit is contained in:
parent
b999d59bfc
commit
3f51efcebe
|
@ -1,28 +1,31 @@
|
||||||
{
|
{
|
||||||
"name" : "mall-app-t",
|
"name": "mall-app-t",
|
||||||
"appid" : "__UNI__B201544",
|
"appid": "__UNI__B201544",
|
||||||
"description" : "",
|
"description": "",
|
||||||
"versionName" : "1.0.0",
|
"versionName": "1.0.0",
|
||||||
"versionCode" : "100",
|
"versionCode": "100",
|
||||||
"transformPx" : false,
|
"transformPx": false,
|
||||||
/* 5+App特有相关 */
|
/* 5+App特有相关 */
|
||||||
"app-plus" : {
|
"app-plus": {
|
||||||
"usingComponents" : true,
|
"usingComponents": true,
|
||||||
"nvueStyleCompiler" : "uni-app",
|
"nvueStyleCompiler": "uni-app",
|
||||||
"compilerVersion" : 3,
|
"compilerVersion": 3,
|
||||||
"splashscreen" : {
|
"splashscreen": {
|
||||||
"alwaysShowBeforeRender" : true,
|
"alwaysShowBeforeRender": true,
|
||||||
"waiting" : true,
|
"waiting": true,
|
||||||
"autoclose" : true,
|
"autoclose": true,
|
||||||
"delay" : 0
|
"delay": 0
|
||||||
|
},
|
||||||
|
"compatible": {
|
||||||
|
"ignoreVersion": true
|
||||||
},
|
},
|
||||||
/* 模块配置 */
|
/* 模块配置 */
|
||||||
"modules" : {},
|
"modules": {},
|
||||||
/* 应用发布信息 */
|
/* 应用发布信息 */
|
||||||
"distribute" : {
|
"distribute": {
|
||||||
/* android打包配置 */
|
/* android打包配置 */
|
||||||
"android" : {
|
"android": {
|
||||||
"permissions" : [
|
"permissions": [
|
||||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||||
|
@ -41,32 +44,32 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
/* ios打包配置 */
|
/* ios打包配置 */
|
||||||
"ios" : {},
|
"ios": {},
|
||||||
/* SDK配置 */
|
/* SDK配置 */
|
||||||
"sdkConfigs" : {}
|
"sdkConfigs": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/* 快应用特有相关 */
|
/* 快应用特有相关 */
|
||||||
"quickapp" : {},
|
"quickapp": {},
|
||||||
/* 小程序特有相关 */
|
/* 小程序特有相关 */
|
||||||
"mp-weixin" : {
|
"mp-weixin": {
|
||||||
"appid" : "wx64387dc8bba916ec",
|
"appid": "wx64387dc8bba916ec",
|
||||||
"setting" : {
|
"setting": {
|
||||||
"urlCheck" : false
|
"urlCheck": false
|
||||||
},
|
},
|
||||||
"usingComponents" : true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"mp-alipay" : {
|
"mp-alipay": {
|
||||||
"usingComponents" : true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"mp-baidu" : {
|
"mp-baidu": {
|
||||||
"usingComponents" : true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"mp-toutiao" : {
|
"mp-toutiao": {
|
||||||
"usingComponents" : true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"uniStatistics" : {
|
"uniStatistics": {
|
||||||
"enable" : false
|
"enable": false
|
||||||
},
|
},
|
||||||
"vueVersion" : "3"
|
"vueVersion": "3"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,43 @@
|
||||||
<template>
|
<template>
|
||||||
<pb-layout class="product-list" title="产品" navbar="normal" tabbar="/pages/index/product" :bgStyle="bgStyle"
|
<pb-layout
|
||||||
opacityBgUi="bg-white" color="black">
|
class="product-list"
|
||||||
<view v-if="state.pagination.total > 0" class="goods-list ss-m-t-20">
|
title="产品"
|
||||||
<view class="ss-p-l-20 ss-p-r-20 ss-m-b-20" v-for="item in state.pagination.list" :key="item.id">
|
navbar="normal"
|
||||||
<p-goods-column size="lg" :data="item" :topRadius="10" :bottomRadius="10"
|
tabbar="/pages/index/product"
|
||||||
@click="peach.$router.go('/pages/product/manageGoods', { id: item.id, mark: 'detail' })" />
|
:bgStyle="bgStyle"
|
||||||
</view>
|
opacityBgUi="bg-white"
|
||||||
</view>
|
color="black"
|
||||||
|
>
|
||||||
|
<view v-if="state.pagination.total > 0" class="goods-list ss-m-t-20">
|
||||||
|
<view class="ss-p-l-20 ss-p-r-20 ss-m-b-20" v-for="item in state.pagination.list" :key="item.id">
|
||||||
|
<p-goods-column
|
||||||
|
size="lg"
|
||||||
|
:data="item"
|
||||||
|
:topRadius="10"
|
||||||
|
:bottomRadius="10"
|
||||||
|
@click="peach.$router.go('/pages/product/manageGoods', { id: item.id, mark: 'detail' })"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
|
<uni-load-more
|
||||||
contentdown: '上拉加载更多',
|
v-if="state.pagination.total > 0"
|
||||||
}" @click="loadMore" />
|
:status="state.loadStatus"
|
||||||
|
:content-text="{
|
||||||
|
contentdown: '上拉加载更多',
|
||||||
|
}"
|
||||||
|
@click="loadMore"
|
||||||
|
/>
|
||||||
|
|
||||||
<view class="_icon-add-round add-product" @click="addGoods"></view>
|
<view class="_icon-add-round add-product" @click="addGoods"></view>
|
||||||
|
|
||||||
<p-empty v-if="state.pagination.total === 0" icon="/static/soldout-empty.png" text="暂无产品" bgColor="transparent" />
|
<p-empty
|
||||||
</pb-layout>
|
v-if="state.pagination.total === 0"
|
||||||
|
icon="/static/soldout-empty.png"
|
||||||
|
text="暂无产品"
|
||||||
|
bgColor="transparent"
|
||||||
|
/>
|
||||||
|
</pb-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
@ -27,167 +49,172 @@ import _ from 'lodash'
|
||||||
import { resetPagination } from '@/peach/utils'
|
import { resetPagination } from '@/peach/utils'
|
||||||
|
|
||||||
const bgStyle = {
|
const bgStyle = {
|
||||||
backgroundImage: '',
|
backgroundImage: '',
|
||||||
backgroundColor: 'var(--ui-BG-1)',
|
backgroundColor: 'var(--ui-BG-1)',
|
||||||
description: '',
|
description: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = ref({
|
const state = ref({
|
||||||
pagination: {
|
pagination: {
|
||||||
list: [],
|
list: [],
|
||||||
total: 0,
|
total: 0,
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 6,
|
pageSize: 6,
|
||||||
name: '',
|
name: '',
|
||||||
createTime: [],
|
createTime: [],
|
||||||
},
|
},
|
||||||
loadStatus: '',
|
loadStatus: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
function emptyList() {
|
function emptyList() {
|
||||||
resetPagination(state.value.pagination)
|
resetPagination(state.value.pagination)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSearch() {
|
function onSearch() {
|
||||||
emptyList()
|
emptyList()
|
||||||
getList()
|
getList()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getList() {
|
async function getList() {
|
||||||
let { data } = await GoodApi.getProductList({
|
let { data } = await GoodApi.getProductList({
|
||||||
pageNo: state.value.pagination.pageNo,
|
pageNo: state.value.pagination.pageNo,
|
||||||
pageSize: state.value.pagination.pageSize,
|
pageSize: state.value.pagination.pageSize,
|
||||||
})
|
})
|
||||||
|
|
||||||
state.value.pagination.list = _.concat(state.value.pagination.list, data.list)
|
state.value.pagination.list = _.concat(state.value.pagination.list, data.list)
|
||||||
state.value.pagination.total = data.total
|
state.value.pagination.total = data.total
|
||||||
let currentPageTotal = state.value.pagination.length
|
let currentPageTotal = state.value.pagination.length
|
||||||
|
|
||||||
state.value.loadStatus = currentPageTotal < state.value.pagination.total ? 'more' : 'noMore'
|
state.value.loadStatus = currentPageTotal < state.value.pagination.total ? 'more' : 'noMore'
|
||||||
}
|
}
|
||||||
|
|
||||||
function addGoods() {
|
function addGoods() {
|
||||||
peach.$router.go('/pages/product/manageGoods', {
|
peach.$store('trade').$patch({
|
||||||
title: '添加商品'
|
selectedProperty: null,
|
||||||
})
|
goodsInfo: null,
|
||||||
|
skus: null,
|
||||||
|
})
|
||||||
|
peach.$router.go('/pages/product/manageGoods', {
|
||||||
|
title: '添加商品',
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadMore() {
|
function loadMore() {
|
||||||
if (state.value.loadStatus === 'noMore') {
|
if (state.value.loadStatus === 'noMore') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
state.value.pagination.pageNo++
|
state.value.pagination.pageNo++
|
||||||
getList()
|
getList()
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoad(async (options) => {
|
onLoad(async (options) => {
|
||||||
getList()
|
getList()
|
||||||
})
|
})
|
||||||
|
|
||||||
onReachBottom(() => {
|
onReachBottom(() => {
|
||||||
loadMore()
|
loadMore()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.product-list {
|
.product-list {
|
||||||
.add-product {
|
.add-product {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
color: var(--ui-BG-Main);
|
color: var(--ui-BG-Main);
|
||||||
bottom: 70px;
|
bottom: 70px;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
font-size: 80rpx;
|
font-size: 80rpx;
|
||||||
}
|
|
||||||
|
|
||||||
.goods-list-box {
|
|
||||||
width: 50%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.left-list {
|
|
||||||
margin-right: 10rpx;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-list {
|
.goods-list-box {
|
||||||
margin-left: 10rpx;
|
width: 50%;
|
||||||
margin-bottom: 20rpx;
|
box-sizing: border-box;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-box {
|
.left-list {
|
||||||
&:nth-last-of-type(1) {
|
margin-right: 10rpx;
|
||||||
margin-bottom: 0 !important;
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-list {
|
||||||
|
margin-left: 10rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:nth-child(2n) {
|
.goods-box {
|
||||||
margin-right: 0;
|
&:nth-last-of-type(1) {
|
||||||
}
|
margin-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-icon {
|
&:nth-child(2n) {
|
||||||
width: 80rpx;
|
margin-right: 0;
|
||||||
|
}
|
||||||
.sicon-goods-card {
|
|
||||||
font-size: 40rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sicon-goods-list {
|
.list-icon {
|
||||||
font-size: 40rpx;
|
width: 80rpx;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-card {
|
.sicon-goods-card {
|
||||||
margin-left: 20rpx;
|
font-size: 40rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-filter-tabs {
|
.sicon-goods-list {
|
||||||
background-color: #fff;
|
font-size: 40rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-list-box {
|
|
||||||
padding: 28rpx 52rpx;
|
|
||||||
|
|
||||||
.filter-item {
|
|
||||||
font-size: 28rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #333333;
|
|
||||||
line-height: normal;
|
|
||||||
margin-bottom: 24rpx;
|
|
||||||
|
|
||||||
&:nth-last-child(1) {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-item-active {
|
.goods-card {
|
||||||
color: var(--ui-BG-Main);
|
margin-left: 20rpx;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-item {
|
|
||||||
height: 50px;
|
|
||||||
position: relative;
|
|
||||||
z-index: 11;
|
|
||||||
|
|
||||||
.tab-title {
|
|
||||||
font-size: 30rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cur-tab-title {
|
.list-filter-tabs {
|
||||||
font-weight: $font-weight-bold;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-line {
|
.filter-list-box {
|
||||||
width: 60rpx;
|
padding: 28rpx 52rpx;
|
||||||
height: 6rpx;
|
|
||||||
border-radius: 6rpx;
|
.filter-item {
|
||||||
position: absolute;
|
font-size: 28rpx;
|
||||||
left: 50%;
|
font-weight: 500;
|
||||||
transform: translateX(-50%);
|
color: #333333;
|
||||||
bottom: 10rpx;
|
line-height: normal;
|
||||||
background-color: var(--ui-BG-Main);
|
margin-bottom: 24rpx;
|
||||||
z-index: 12;
|
|
||||||
|
&:nth-last-child(1) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-item-active {
|
||||||
|
color: var(--ui-BG-Main);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
height: 50px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 11;
|
||||||
|
|
||||||
|
.tab-title {
|
||||||
|
font-size: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cur-tab-title {
|
||||||
|
font-weight: $font-weight-bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-line {
|
||||||
|
width: 60rpx;
|
||||||
|
height: 6rpx;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
bottom: 10rpx;
|
||||||
|
background-color: var(--ui-BG-Main);
|
||||||
|
z-index: 12;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<view></view>
|
<view>页面重定向</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import $store from '@/peach/store'
|
|
||||||
import peach from '@/peach'
|
import peach from '@/peach'
|
||||||
|
|
||||||
const userStore = $store('user')
|
|
||||||
|
|
||||||
async function redirectFn() {
|
async function redirectFn() {
|
||||||
|
const userStore = peach.$store('user')
|
||||||
|
|
||||||
// 判断是否登录
|
// 判断是否登录
|
||||||
if (!userStore.isLogin) {
|
if (!userStore.isLogin) {
|
||||||
userStore.logOut()
|
userStore.logOut()
|
||||||
|
|
|
@ -1,80 +1,126 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="sku-item">
|
<div class="sku-item">
|
||||||
<uni-forms label-width="176rpx" label-position="left">
|
<uni-forms label-width="176rpx" label-position="left">
|
||||||
|
<template v-if="specType">
|
||||||
|
<template v-for="item in formData.properties">
|
||||||
|
<uni-forms-item :label="item.propertyName">
|
||||||
|
<uni-easyinput type="text" :value="item.valueName" disabled />
|
||||||
|
</uni-forms-item>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template v-if="specType">
|
<uni-forms-item label="商品封面图" name="picUrl" label-position="top">
|
||||||
<template v-for="item in formData.properties">
|
<p-uploader
|
||||||
<uni-forms-item :label="item.propertyName">
|
v-model:url="formData.picUrl"
|
||||||
<uni-easyinput type="text" :value="item.valueName" disabled />
|
:readonly="!canEdit"
|
||||||
</uni-forms-item>
|
fileMediatype="image"
|
||||||
</template>
|
limit="1"
|
||||||
</template>
|
mode="grid"
|
||||||
|
:imageStyles="{ width: '168rpx', height: '168rpx' }"
|
||||||
<uni-forms-item label="商品封面图" name="picUrl" label-position="top">
|
/>
|
||||||
<p-uploader v-model:url="formData.picUrl" :readonly="!canEdit" fileMediatype="image" limit="1" mode="grid"
|
</uni-forms-item>
|
||||||
:imageStyles="{ width: '168rpx', height: '168rpx' }" />
|
<uni-forms-item label="商品条码" name="barCode">
|
||||||
</uni-forms-item>
|
<uni-easyinput
|
||||||
<uni-forms-item label="商品条码" name="barCode">
|
type="text"
|
||||||
<uni-easyinput type="text" trim="all" v-model="formData.barCode" :disabled="!canEdit" placeholder="请输入商品条码" />
|
trim="all"
|
||||||
</uni-forms-item>
|
v-model="formData.barCode"
|
||||||
<uni-forms-item label="销售价" name="price">
|
:disabled="!canEdit"
|
||||||
<uni-easyinput type="number" trim="all" v-model="formData.price" :disabled="!canEdit" placeholder="请输入商品销售价" />
|
placeholder="请输入商品条码"
|
||||||
</uni-forms-item>
|
/>
|
||||||
<uni-forms-item label="市场价" name="marketPrice">
|
</uni-forms-item>
|
||||||
<uni-easyinput type="number" trim="all" v-model="formData.marketPrice" :disabled="!canEdit"
|
<uni-forms-item label="销售价" name="price">
|
||||||
placeholder="请输入商品销售价" />
|
<uni-easyinput
|
||||||
</uni-forms-item>
|
type="digit"
|
||||||
<uni-forms-item label="成本价" name="costPrice">
|
trim="all"
|
||||||
<uni-easyinput type="number" trim="all" v-model="formData.costPrice" :disabled="!canEdit"
|
v-model="formData.price"
|
||||||
placeholder="请输入商品销售价" />
|
:disabled="!canEdit"
|
||||||
</uni-forms-item>
|
placeholder="请输入商品销售价"
|
||||||
<uni-forms-item label="库存" name="stock">
|
/>
|
||||||
<uni-easyinput type="number" trim="all" v-model="formData.stock" :disabled="!canEdit" placeholder="请输入商品库存" />
|
</uni-forms-item>
|
||||||
</uni-forms-item>
|
<uni-forms-item label="市场价" name="marketPrice">
|
||||||
<uni-forms-item label="重量(kg)" name="weight">
|
<uni-easyinput
|
||||||
<uni-easyinput type="number" trim="all" v-model="formData.weight" :disabled="!canEdit" placeholder="请输入商品重量" />
|
type="digit"
|
||||||
</uni-forms-item>
|
trim="all"
|
||||||
<uni-forms-item label="体积(m³)" name="volume">
|
v-model="formData.marketPrice"
|
||||||
<uni-easyinput type="number" trim="all" v-model="formData.volume" :disabled="!canEdit" placeholder="请输入商品体积" />
|
:disabled="!canEdit"
|
||||||
</uni-forms-item>
|
placeholder="请输入商品销售价"
|
||||||
</uni-forms>
|
/>
|
||||||
</div>
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="成本价" name="costPrice">
|
||||||
|
<uni-easyinput
|
||||||
|
type="digit"
|
||||||
|
trim="all"
|
||||||
|
v-model="formData.costPrice"
|
||||||
|
:disabled="!canEdit"
|
||||||
|
placeholder="请输入商品销售价"
|
||||||
|
/>
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="库存" name="stock">
|
||||||
|
<uni-easyinput
|
||||||
|
type="number"
|
||||||
|
trim="all"
|
||||||
|
v-model="formData.stock"
|
||||||
|
:disabled="!canEdit"
|
||||||
|
placeholder="请输入商品库存"
|
||||||
|
/>
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="重量(kg)" name="weight">
|
||||||
|
<uni-easyinput
|
||||||
|
type="digit"
|
||||||
|
trim="all"
|
||||||
|
v-model="formData.weight"
|
||||||
|
:disabled="!canEdit"
|
||||||
|
placeholder="请输入商品重量"
|
||||||
|
/>
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="体积(m³)" name="volume">
|
||||||
|
<uni-easyinput
|
||||||
|
type="digit"
|
||||||
|
trim="all"
|
||||||
|
v-model="formData.volume"
|
||||||
|
:disabled="!canEdit"
|
||||||
|
placeholder="请输入商品体积"
|
||||||
|
/>
|
||||||
|
</uni-forms-item>
|
||||||
|
</uni-forms>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, computed, defineProps } from 'vue'
|
import { ref, watch, computed, defineProps } from 'vue'
|
||||||
import peach from '@/peach'
|
import peach from '@/peach'
|
||||||
import { canEdit } from '../js/sku';
|
import { canEdit } from '../js/sku'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
skus: {
|
skus: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => [],
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
const formData = ref({})
|
const formData = ref({})
|
||||||
const specType = computed(() => peach.$store("trade").goodsInfo.specType);
|
const specType = computed(() => peach.$store('trade').goodsInfo?.specType || false)
|
||||||
|
|
||||||
watch(() => props.skus, (newVal) => {
|
|
||||||
console.log(newVal)
|
|
||||||
// 如果是单规格,取 sku 第一条数据
|
|
||||||
if (!specType.value) {
|
|
||||||
formData.value = newVal[0] ?? {}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
formData.value = newVal ?? {}
|
|
||||||
}, { immediate: true })
|
|
||||||
|
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.skus,
|
||||||
|
(newVal) => {
|
||||||
|
console.log(newVal)
|
||||||
|
// 如果是单规格,取 sku 第一条数据
|
||||||
|
if (!specType.value) {
|
||||||
|
if (newVal) formData.value = newVal[0] ?? {}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
formData.value = newVal ?? {}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.sku-item {
|
.sku-item {
|
||||||
margin: 40rpx;
|
margin: 40rpx;
|
||||||
padding-top: 40rpx;
|
padding-top: 40rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sku-item:first-child {
|
.sku-item:first-child {
|
||||||
border-top: 1px solid var(--ui-BG-Main);
|
border-top: 1px solid var(--ui-BG-Main);
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -8,3 +8,27 @@ export const SPEC_TYPE = [
|
||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// sku 相关属性校验
|
||||||
|
export const SKU_RULE_CONFIG = [
|
||||||
|
{
|
||||||
|
name: 'stock',
|
||||||
|
rule: (arg) => arg >= 0,
|
||||||
|
message: '商品库存必须大于等于 1 !!!',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'price',
|
||||||
|
rule: (arg) => arg >= 0.01,
|
||||||
|
message: '商品销售价格必须大于等于 0.01 元!!!',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'marketPrice',
|
||||||
|
rule: (arg) => arg >= 0.01,
|
||||||
|
message: '商品市场价格必须大于等于 0.01 元!!!',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'costPrice',
|
||||||
|
rule: (arg) => arg >= 0.01,
|
||||||
|
message: '商品成本价格必须大于等于 0.00 元!!!',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
|
@ -1,179 +1,277 @@
|
||||||
import { ref, computed } from "vue";
|
import { ref, computed } from 'vue'
|
||||||
import { onLoad } from "@dcloudio/uni-app";
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import peach from "@/peach";
|
import peach from '@/peach'
|
||||||
import GoodsApi from "@/peach/api/trade/goods";
|
import GoodsApi from '@/peach/api/trade/goods'
|
||||||
import { SPEC_TYPE } from "./config";
|
import { SPEC_TYPE, SKU_RULE_CONFIG } from './config'
|
||||||
|
|
||||||
const pickerRef = ref(null);
|
const pickerRef = ref(null)
|
||||||
|
|
||||||
// 多属性商品 sku 列表
|
// 多属性商品 sku 列表
|
||||||
const skus = ref([]);
|
const skus = ref([])
|
||||||
|
|
||||||
const propertyList = ref([]);
|
const propertyList = ref([])
|
||||||
|
|
||||||
const goodsPropertyList = ref([]);
|
const goodsPropertyList = ref([])
|
||||||
|
|
||||||
const propertyListRef = ref(null);
|
const propertyListRef = ref(null)
|
||||||
|
|
||||||
const canEdit = computed(() => peach.$store("trade").canEdit);
|
const canEdit = computed(() => peach.$store('trade').canEdit)
|
||||||
|
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
specType: true,
|
specType: true,
|
||||||
specText: SPEC_TYPE[0].label,
|
specText: SPEC_TYPE[0].label,
|
||||||
});
|
})
|
||||||
|
|
||||||
async function showPropertyList() {
|
async function showPropertyList() {
|
||||||
await getGoodsProperty();
|
await getGoodsProperty()
|
||||||
propertyListRef.value.onOpen();
|
propertyListRef.value.onOpen()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onRDPickerConfirm(e) {
|
function onRDPickerConfirm(e) {
|
||||||
peach.$store("trade").specType = SPEC_TYPE[e.value[0]].value;
|
formData.value.specType = SPEC_TYPE[e.value[0]].value
|
||||||
formData.value.specText = SPEC_TYPE[e.value[0]].label;
|
formData.value.specText = SPEC_TYPE[e.value[0]].label
|
||||||
formData.value.specType = SPEC_TYPE[e.value[0]].value;
|
|
||||||
|
// 如果商品规格不一致,则需要重新初始化 sku 列表
|
||||||
|
initSku()
|
||||||
|
|
||||||
|
peach.$store('trade').specType = SPEC_TYPE[e.value[0]].value
|
||||||
}
|
}
|
||||||
|
|
||||||
function pickerProperty() {
|
function pickerProperty() {
|
||||||
if (canEdit.value) {
|
if (canEdit.value) {
|
||||||
let index = specType.value ? 1 : 0;
|
let index = formData.value.specType ? 1 : 0
|
||||||
pickerRef.value.onOpen([index]);
|
|
||||||
}
|
console.log(index)
|
||||||
|
|
||||||
|
pickerRef.value.onOpen([index])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onPropertyConfirm(e) {
|
async function onPropertyConfirm(e) {
|
||||||
await getGoodsProperty();
|
await getGoodsProperty()
|
||||||
console.log(e);
|
console.log(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onConfirm() {
|
function onConfirm() {
|
||||||
console.log(skus.value);
|
console.log(skus.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getGoodsProperty() {
|
async function getGoodsProperty() {
|
||||||
let { data } = await GoodsApi.getHistoryProperty();
|
let { data } = await GoodsApi.getHistoryProperty()
|
||||||
|
|
||||||
// 把 propertyList 中 id 相同的属性合并,并去重
|
// 把 propertyList 中 id 相同的属性合并,并去重
|
||||||
propertyList.value = peach.$store("trade").selectedProperty;
|
propertyList.value = peach.$store('trade').selectedProperty
|
||||||
|
|
||||||
console.log(propertyList.value);
|
console.log(propertyList.value)
|
||||||
|
|
||||||
// 根据已经选择数据,设置默认选中
|
// 根据已经选择数据,设置默认选中
|
||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
// 判断属性是否已经选中
|
// 判断属性是否已经选中
|
||||||
let propertyParent = propertyList.value.find(
|
let propertyParent = propertyList.value.find((sitem) => sitem?.id === item.id)
|
||||||
(sitem) => sitem?.id === item.id
|
|
||||||
);
|
|
||||||
|
|
||||||
item.checked = propertyParent ? true : false;
|
item.checked = propertyParent ? true : false
|
||||||
|
|
||||||
// 如果属性已经选中,查询子类中是否有选中
|
// 如果属性已经选中,查询子类中是否有选中
|
||||||
if (item.checked) {
|
if (item.checked) {
|
||||||
item.propertyValues.forEach((child) => {
|
item.propertyValues.forEach((child) => {
|
||||||
let childResult = propertyParent?.children.some(
|
let childResult = propertyParent?.children.some((schild) => schild === child.id)
|
||||||
(schild) => schild === child.id
|
child.checked = childResult ? true : false
|
||||||
);
|
})
|
||||||
child.checked = childResult ? true : false;
|
}
|
||||||
});
|
})
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
goodsPropertyList.value = data;
|
goodsPropertyList.value = data
|
||||||
console.log(goodsPropertyList.value);
|
console.log(goodsPropertyList.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeSubProperty() {
|
function changeSubProperty() {
|
||||||
// 修改子属性状态,需要同步更新 skus 的显示
|
// 修改子属性状态,需要同步更新 skus 的显示
|
||||||
console.log(goodsPropertyList.value);
|
console.log(goodsPropertyList.value)
|
||||||
// 过滤父属性 checked 选项,深拷贝避免后面循环改变元数据内容
|
// 过滤父属性 checked 选项,深拷贝避免后面循环改变元数据内容
|
||||||
let temp = JSON.parse(
|
let temp = JSON.parse(JSON.stringify(goodsPropertyList.value.filter((item) => item.checked)))
|
||||||
JSON.stringify(goodsPropertyList.value.filter((item) => item.checked))
|
temp.forEach((item) => {
|
||||||
);
|
item.propertyValues = item.propertyValues.filter((child) => child.checked)
|
||||||
temp.forEach((item) => {
|
})
|
||||||
item.propertyValues = item.propertyValues.filter((child) => child.checked);
|
|
||||||
});
|
|
||||||
|
|
||||||
let result = temp.map((item) => {
|
let result = temp.map((item) => {
|
||||||
return item.propertyValues.map((child) => ({
|
return item.propertyValues.map((child) => ({
|
||||||
propertyId: item.id,
|
propertyId: item.id,
|
||||||
propertyName: item.name,
|
propertyName: item.name,
|
||||||
valueId: child.id,
|
valueId: child.id,
|
||||||
valueName: child.name,
|
valueName: child.name,
|
||||||
}));
|
}))
|
||||||
});
|
})
|
||||||
|
|
||||||
let tempSkus = [];
|
let tempSkus = []
|
||||||
|
|
||||||
for (let item of reduceArr(result)) {
|
for (let item of reduceArr(result)) {
|
||||||
let obj = {
|
let obj = {
|
||||||
picUrl: "",
|
picUrl: '',
|
||||||
barCode: "",
|
barCode: '',
|
||||||
price: 0,
|
price: 0,
|
||||||
marketPrice: 0,
|
marketPrice: 0,
|
||||||
costPrice: 0,
|
costPrice: 0,
|
||||||
stock: 0,
|
stock: 0,
|
||||||
weight: 0,
|
weight: 0,
|
||||||
volume: 0,
|
volume: 0,
|
||||||
properties: item,
|
properties: item,
|
||||||
};
|
}
|
||||||
tempSkus.push(obj);
|
tempSkus.push(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
skus.value = tempSkus;
|
skus.value = tempSkus
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Ankkaya
|
||||||
|
* @description 新增商品初始化商品 sku
|
||||||
|
* @param {Type} -
|
||||||
|
* @returns {Type}
|
||||||
|
*/
|
||||||
|
function initSku() {
|
||||||
|
// 单规格
|
||||||
|
if (!formData.value.specType) {
|
||||||
|
let obj = {
|
||||||
|
picUrl: '',
|
||||||
|
barCode: '',
|
||||||
|
price: 0,
|
||||||
|
marketPrice: 0,
|
||||||
|
costPrice: 0,
|
||||||
|
stock: 0,
|
||||||
|
weight: null,
|
||||||
|
volume: null,
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
propertyId: 0,
|
||||||
|
propertyName: '默认',
|
||||||
|
valueId: 0,
|
||||||
|
valueName: '默认',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
skus.value = [obj]
|
||||||
|
} else {
|
||||||
|
// 多规格
|
||||||
|
skus.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Ankkaya
|
||||||
|
* @description 确认属性
|
||||||
|
* @param {Type} -
|
||||||
|
* @returns {Type}
|
||||||
|
*/
|
||||||
|
function submitProperty() {
|
||||||
|
try {
|
||||||
|
validateSku(skus.value)
|
||||||
|
peach.$store('trade').skus = skus.value
|
||||||
|
peach.$router.back()
|
||||||
|
} catch (e) {
|
||||||
|
console.log(skus.value)
|
||||||
|
console.log(e, '校验失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateSku(skus) {
|
||||||
|
let warningInfo = '请检查商品各行相关属性配置,'
|
||||||
|
let validateStatue = true
|
||||||
|
let skusValue = skus ?? peach.$store('trade').skus
|
||||||
|
for (const sku of skusValue) {
|
||||||
|
for (const rule of SKU_RULE_CONFIG) {
|
||||||
|
const arg = getValue(sku, rule.name)
|
||||||
|
if (!rule.rule(arg)) {
|
||||||
|
validateStatue = false
|
||||||
|
warningInfo += rule.message
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!validateStatue) {
|
||||||
|
uni.showToast({
|
||||||
|
title: warningInfo,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 4000,
|
||||||
|
})
|
||||||
|
throw new Error(warningInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getValue(obj, arg) {
|
||||||
|
const keys = arg.split('.')
|
||||||
|
let value = obj
|
||||||
|
for (const key of keys) {
|
||||||
|
if (value && typeof value === 'object' && key in value) {
|
||||||
|
value = value[key]
|
||||||
|
} else {
|
||||||
|
value = undefined
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
function reduceArr(arr) {
|
function reduceArr(arr) {
|
||||||
return arr.reduce((acc, cur) => {
|
return arr.reduce((acc, cur) => {
|
||||||
let tempAcc = [];
|
let tempAcc = []
|
||||||
|
|
||||||
if (acc.length < 1) {
|
if (acc.length < 1) {
|
||||||
cur.forEach((item, index) => {
|
cur.forEach((item, index) => {
|
||||||
if (tempAcc[index]) {
|
if (tempAcc[index]) {
|
||||||
tempAcc[index].push(item);
|
tempAcc[index].push(item)
|
||||||
|
} else {
|
||||||
|
tempAcc[index] = [item]
|
||||||
|
}
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
tempAcc[index] = [item];
|
acc.forEach((item, index) => {
|
||||||
}
|
cur.forEach((sitem, sindex) => {
|
||||||
});
|
tempAcc.push([...item, sitem])
|
||||||
} else {
|
})
|
||||||
acc.forEach((item, index) => {
|
})
|
||||||
cur.forEach((sitem, sindex) => {
|
|
||||||
tempAcc.push([...item, sitem]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (cur.length < 1) {
|
if (cur.length < 1) {
|
||||||
tempAcc = acc;
|
tempAcc = acc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tempAcc;
|
return tempAcc
|
||||||
}, []);
|
}, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
const specType = computed(() => peach.$store("trade").goodsInfo.specType);
|
const specType = computed(() => peach.$store('trade').goodsInfo?.specType || false)
|
||||||
|
|
||||||
function initial() {
|
function initial() {
|
||||||
onLoad(() => {
|
onLoad(() => {
|
||||||
formData.value.specType = specType.value ? true : false;
|
formData.value.specType = specType.value ? true : false
|
||||||
formData.value.specText = SPEC_TYPE[specType.value ? 1 : 0].label;
|
formData.value.specText = SPEC_TYPE[specType.value ? 1 : 0].label
|
||||||
skus.value = peach.$store("trade").skus;
|
skus.value = JSON.parse(JSON.stringify(peach.$store('trade').skus))
|
||||||
if (specType.value) {
|
// 如果新增商品 sku,并且是单规格,初始化 sku
|
||||||
getGoodsProperty();
|
|
||||||
}
|
if (!skus.value) {
|
||||||
});
|
initSku()
|
||||||
|
}
|
||||||
|
if (specType.value) {
|
||||||
|
getGoodsProperty()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
initial,
|
initial,
|
||||||
canEdit,
|
canEdit,
|
||||||
skus,
|
skus,
|
||||||
pickerRef,
|
pickerRef,
|
||||||
pickerProperty,
|
pickerProperty,
|
||||||
onConfirm,
|
validateSku,
|
||||||
propertyListRef,
|
onConfirm,
|
||||||
formData,
|
submitProperty,
|
||||||
onRDPickerConfirm,
|
propertyListRef,
|
||||||
onPropertyConfirm,
|
formData,
|
||||||
propertyList,
|
onRDPickerConfirm,
|
||||||
showPropertyList,
|
onPropertyConfirm,
|
||||||
goodsPropertyList,
|
propertyList,
|
||||||
changeSubProperty,
|
showPropertyList,
|
||||||
};
|
goodsPropertyList,
|
||||||
|
changeSubProperty,
|
||||||
|
}
|
||||||
|
|
|
@ -1,187 +1,285 @@
|
||||||
<template>
|
<template>
|
||||||
<pb-layout class="manage-goods" :title="goodsTitle" leftIcon="leftIcon" navbar="normal" :bgStyle="bgStyle"
|
<pb-layout
|
||||||
opacityBgUi="bg-white" color="black">
|
class="manage-goods"
|
||||||
<view class="goods-form">
|
:title="goodsTitle"
|
||||||
<uni-forms ref="formRef" v-model="formData" :rules="rules" label-position="top" label-width="160">
|
leftIcon="leftIcon"
|
||||||
<uni-forms-item label="商品封面图" name="picUrl" required>
|
navbar="normal"
|
||||||
<p-uploader v-model:url="formData.picUrl" :readonly="!canEdit" fileMediatype="image" limit="1" mode="grid"
|
:bgStyle="bgStyle"
|
||||||
:imageStyles="{ width: '168rpx', height: '168rpx' }" />
|
opacityBgUi="bg-white"
|
||||||
</uni-forms-item>
|
color="black"
|
||||||
<uni-forms-item label="商品轮播图" name="sliderPicUrls" required>
|
>
|
||||||
<p-uploader v-model:url="formData.sliderPicUrls" :readonly="!canEdit" fileMediatype="image" limit="6"
|
<view class="goods-form">
|
||||||
mode="grid" :imageStyles="{ width: '168rpx', height: '168rpx' }" />
|
<uni-forms ref="formRef" v-model="formData" :rules="rules" label-position="top" label-width="160">
|
||||||
</uni-forms-item>
|
<uni-forms-item label="商品封面图" name="picUrl" required>
|
||||||
<uni-forms-item label="商品名称" name="name" required>
|
<p-uploader
|
||||||
<uni-easyinput type="text" trim="all" v-model="formData.name" :disabled="!canEdit" placeholder="请输入商品名称" />
|
v-model:url="formData.picUrl"
|
||||||
</uni-forms-item>
|
:readonly="!canEdit"
|
||||||
<uni-forms-item label="商品分类" @tap="openPicker('category', 'multiple')" name="categoryId" label-position="left"
|
fileMediatype="image"
|
||||||
required>
|
limit="1"
|
||||||
<uni-easyinput type="text" v-model="formData.categoryText" :disabled="!canEdit" :styles="selfStyles"
|
mode="grid"
|
||||||
placeholderStyle="color:#8a8a8a" :clearable="false" :inputBorder="false" placeholder="请选择商品分类" disabled>
|
:imageStyles="{ width: '168rpx', height: '168rpx' }"
|
||||||
<template v-slot:right>
|
/>
|
||||||
<uni-icons type="right" />
|
</uni-forms-item>
|
||||||
</template>
|
<uni-forms-item label="商品轮播图" name="sliderPicUrls" required>
|
||||||
</uni-easyinput>
|
<p-uploader
|
||||||
</uni-forms-item>
|
v-model:url="formData.sliderPicUrls"
|
||||||
<uni-forms-item label="商品品牌" name="brandId" label-position="left" required @tap="openPicker('brand', 'single')">
|
:readonly="!canEdit"
|
||||||
<uni-easyinput type="text" v-model="formData.brandText" :disabled="!canEdit" :styles="selfStyles"
|
fileMediatype="image"
|
||||||
placeholderStyle="color:#8a8a8a" :clearable="false" :inputBorder="false" placeholder="请选择商品品牌" disabled>
|
limit="6"
|
||||||
<template v-slot:right>
|
mode="grid"
|
||||||
<uni-icons type="right" />
|
:imageStyles="{ width: '168rpx', height: '168rpx' }"
|
||||||
</template>
|
/>
|
||||||
</uni-easyinput>
|
</uni-forms-item>
|
||||||
</uni-forms-item>
|
<uni-forms-item label="商品名称" name="name" required>
|
||||||
<uni-forms-item label="商品规格" name="skus" required label-position="left">
|
<uni-easyinput
|
||||||
<view class="btn-group">
|
type="text"
|
||||||
<button class="ss-reset-button ss-set-property" @tap="clickSetProperty">规格设置</button>
|
trim="all"
|
||||||
</view>
|
v-model="formData.name"
|
||||||
</uni-forms-item>
|
:disabled="!canEdit"
|
||||||
<uni-forms-item label="商品关键词" name="keyword" required>
|
placeholder="请输入商品名称"
|
||||||
<uni-easyinput type="text" v-model="formData.keyword" :disabled="!canEdit" placeholder="请输入商品关键词" />
|
/>
|
||||||
</uni-forms-item>
|
</uni-forms-item>
|
||||||
<uni-forms-item label="商品简介" name="introduction" required>
|
<uni-forms-item
|
||||||
<uni-easyinput type="textarea" :disabled="!canEdit" trim="all" autoHeight v-model="formData.introduction"
|
label="商品分类"
|
||||||
placeholder="请输入商品简介" />
|
@tap="openPicker('category', 'multiple')"
|
||||||
</uni-forms-item>
|
name="categoryId"
|
||||||
<uni-forms-item label="物流设置" @tap="openPicker('delivery', 'single')" name="deliveryTypes" label-position="left"
|
label-position="left"
|
||||||
required>
|
required
|
||||||
<uni-easyinput type="text" :clearable="false" :styles="selfStyles" placeholderStyle="color:#8a8a8a"
|
>
|
||||||
:inputBorder="false" v-model="formData.deliveryText" :disabled="!canEdit" placeholder="请选择配送方式" disabled>
|
<uni-easyinput
|
||||||
<template v-slot:right>
|
type="text"
|
||||||
<uni-icons type="right" />
|
v-model="formData.categoryText"
|
||||||
</template>
|
:styles="selfStyles"
|
||||||
</uni-easyinput>
|
placeholderStyle="color:#8a8a8a"
|
||||||
</uni-forms-item>
|
:clearable="false"
|
||||||
</uni-forms>
|
:inputBorder="false"
|
||||||
<view @tap="onSubmit" v-if="canEdit">
|
placeholder="请选择商品分类"
|
||||||
<button class="ss-reset-button submit-button ui-Shadow-Main">提交</button>
|
disabled
|
||||||
</view>
|
>
|
||||||
</view>
|
<template v-slot:right>
|
||||||
|
<uni-icons type="right" />
|
||||||
|
</template>
|
||||||
|
</uni-easyinput>
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item
|
||||||
|
label="商品品牌"
|
||||||
|
name="brandId"
|
||||||
|
label-position="left"
|
||||||
|
required
|
||||||
|
@tap="openPicker('brand', 'single')"
|
||||||
|
>
|
||||||
|
<uni-easyinput
|
||||||
|
type="text"
|
||||||
|
v-model="formData.brandText"
|
||||||
|
:styles="selfStyles"
|
||||||
|
placeholderStyle="color:#8a8a8a"
|
||||||
|
:clearable="false"
|
||||||
|
:inputBorder="false"
|
||||||
|
placeholder="请选择商品品牌"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<template v-slot:right>
|
||||||
|
<uni-icons type="right" />
|
||||||
|
</template>
|
||||||
|
</uni-easyinput>
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="商品规格" name="skus" required label-position="left">
|
||||||
|
<view class="btn-group">
|
||||||
|
<button class="ss-reset-button ss-set-property" @tap="clickSetProperty">规格设置</button>
|
||||||
|
</view>
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="商品关键词" name="keyword" required>
|
||||||
|
<uni-easyinput
|
||||||
|
type="text"
|
||||||
|
v-model="formData.keyword"
|
||||||
|
:disabled="!canEdit"
|
||||||
|
placeholder="请输入商品关键词"
|
||||||
|
/>
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="商品简介" name="introduction" required>
|
||||||
|
<uni-easyinput
|
||||||
|
type="textarea"
|
||||||
|
:disabled="!canEdit"
|
||||||
|
trim="all"
|
||||||
|
autoHeight
|
||||||
|
v-model="formData.introduction"
|
||||||
|
placeholder="请输入商品简介"
|
||||||
|
/>
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item
|
||||||
|
label="物流设置"
|
||||||
|
@tap="openPicker('delivery', 'single')"
|
||||||
|
name="deliveryTypes"
|
||||||
|
label-position="left"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<uni-easyinput
|
||||||
|
type="text"
|
||||||
|
:clearable="false"
|
||||||
|
:styles="selfStyles"
|
||||||
|
placeholderStyle="color:#8a8a8a"
|
||||||
|
:inputBorder="false"
|
||||||
|
v-model="formData.deliveryText"
|
||||||
|
:disabled="!canEdit"
|
||||||
|
placeholder="请选择配送方式"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<template v-slot:right>
|
||||||
|
<uni-icons type="right" />
|
||||||
|
</template>
|
||||||
|
</uni-easyinput>
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="商品详情">
|
||||||
|
<piaoyiEditor
|
||||||
|
:values="richValues"
|
||||||
|
:maxlength="3000"
|
||||||
|
:readOnly="richReadOnly"
|
||||||
|
:photoUrl="photoUrl"
|
||||||
|
:api="richApi"
|
||||||
|
/>
|
||||||
|
</uni-forms-item>
|
||||||
|
</uni-forms>
|
||||||
|
<view @tap="onSubmit" v-if="canEdit">
|
||||||
|
<button class="ss-reset-button submit-button ui-Shadow-Main">提交</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<p-picker ref="pickerRef" :mode="pickerMode" :options-cols="optionsCols" @confirm="onRDPickerConfirm"></p-picker>
|
<p-picker
|
||||||
</pb-layout>
|
ref="pickerRef"
|
||||||
|
:mode="pickerMode"
|
||||||
|
:options-cols="optionsCols"
|
||||||
|
@confirm="onRDPickerConfirm"
|
||||||
|
></p-picker>
|
||||||
|
</pb-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import peach from '@/peach'
|
|
||||||
import { handleTree } from '@/peach/utils'
|
|
||||||
import GoodsApi from '@/peach/api/trade/goods'
|
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
import GoodsApi from '@/peach/api/trade/goods'
|
||||||
|
import piaoyiEditor from '@/uni_modules/piaoyi-editor/components/piaoyi-editor/piaoyi-editor.vue'
|
||||||
|
import peach from '@/peach'
|
||||||
|
import { baseUrl, apiPath } from '@/peach/config'
|
||||||
|
import { handleTree } from '@/peach/utils'
|
||||||
|
import { validateSku } from './js/sku'
|
||||||
|
|
||||||
const bgStyle = {
|
const bgStyle = {
|
||||||
backgroundImage: '',
|
backgroundImage: '',
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
description: '',
|
description: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
const DELIVERY_TYPES = [
|
const DELIVERY_TYPES = [
|
||||||
{
|
{
|
||||||
value: 3,
|
value: 3,
|
||||||
label: '到店核销',
|
label: '到店核销',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 4,
|
value: 4,
|
||||||
label: '商家配送',
|
label: '商家配送',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const selfStyles = {
|
const selfStyles = {
|
||||||
backgroundColor: '#f9f9f9',
|
backgroundColor: '#f9f9f9',
|
||||||
}
|
}
|
||||||
|
|
||||||
const pickerRef = ref()
|
const pickerRef = ref()
|
||||||
|
const richValues = ref('')
|
||||||
|
const photoUrl = baseUrl + apiPath
|
||||||
|
const richApi = '/infra/file/upload'
|
||||||
|
const richReadOnly = ref(false)
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
picUrl: 'http://101.43.181.163:9001/mall-backend/8f11e372520501531d06bfce15ea97bbecead41c5e4a36d15d7e40af85729ff3.png',
|
picUrl: 'http://101.43.181.163:9001/mall-backend/8f11e372520501531d06bfce15ea97bbecead41c5e4a36d15d7e40af85729ff3.png',
|
||||||
sliderPicUrls: [
|
sliderPicUrls: [
|
||||||
'http://101.43.181.163:9001/mall-backend/8f11e372520501531d06bfce15ea97bbecead41c5e4a36d15d7e40af85729ff3.png',
|
'http://101.43.181.163:9001/mall-backend/8f11e372520501531d06bfce15ea97bbecead41c5e4a36d15d7e40af85729ff3.png',
|
||||||
'http://101.43.181.163:9001/mall-backend/8f11e372520501531d06bfce15ea97bbecead41c5e4a36d15d7e40af85729ff3.png',
|
'http://101.43.181.163:9001/mall-backend/8f11e372520501531d06bfce15ea97bbecead41c5e4a36d15d7e40af85729ff3.png',
|
||||||
'http://101.43.181.163:9001/mall-backend/8f11e372520501531d06bfce15ea97bbecead41c5e4a36d15d7e40af85729ff3.png',
|
'http://101.43.181.163:9001/mall-backend/8f11e372520501531d06bfce15ea97bbecead41c5e4a36d15d7e40af85729ff3.png',
|
||||||
],
|
],
|
||||||
name: '测试商品',
|
name: '测试商品',
|
||||||
categoryId: [],
|
categoryId: [],
|
||||||
categoryText: '',
|
categoryText: '',
|
||||||
brandId: '',
|
brandId: '',
|
||||||
keyword: '香酥鸭,但家',
|
keyword: '香酥鸭,但家',
|
||||||
deliveryTypes: [],
|
deliveryTypes: [],
|
||||||
deliveryText: '',
|
deliveryText: '',
|
||||||
introduction: '但家贵阳香酥鸭现榨香酥鸭无任何添加剂香酥鸭但家贵阳香酥鸭现榨香酥鸭无任何添加剂香酥鸭',
|
introduction: '但家贵阳香酥鸭现榨香酥鸭无任何添加剂香酥鸭但家贵阳香酥鸭现榨香酥鸭无任何添加剂香酥鸭',
|
||||||
})
|
})
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
name: {
|
name: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
errorMessage: '请输入商品名称',
|
errorMessage: '请输入商品名称',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
picUrl: {
|
picUrl: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
errorMessage: '请上传商品封面图',
|
errorMessage: '请上传商品封面图',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
sliderPicUrls: {
|
sliderPicUrls: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
errorMessage: '请上传商品轮播图',
|
errorMessage: '请上传商品轮播图',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
categoryId: {
|
categoryId: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
errorMessage: '请选择商品分类',
|
errorMessage: '请选择商品分类',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
brandId: {
|
brandId: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
errorMessage: '请选择商品品牌',
|
errorMessage: '请选择商品品牌',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
skus: {
|
skus: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
errorMessage: '请选择商品规格',
|
errorMessage: '请选择商品规格',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
keyword: {
|
keyword: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
errorMessage: '请输入商品关键字',
|
errorMessage: '请输入商品关键字',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
introduction: {
|
introduction: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
errorMessage: '请输入商品简介',
|
errorMessage: '请输入商品简介',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
deliveryTypes: {
|
deliveryTypes: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
errorMessage: '请选择商品物流',
|
errorMessage: '请选择商品物流',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const formRef = ref(null)
|
const formRef = ref(null)
|
||||||
|
@ -194,223 +292,254 @@ const optionsCols = ref([])
|
||||||
const canEdit = computed(() => peach.$store('trade').canEdit)
|
const canEdit = computed(() => peach.$store('trade').canEdit)
|
||||||
|
|
||||||
function openPicker(mark, mode) {
|
function openPicker(mark, mode) {
|
||||||
if (!canEdit.value) return
|
if (!canEdit.value) return
|
||||||
pickerMode.value = mode
|
pickerMode.value = mode
|
||||||
popMark.value = mark
|
popMark.value = mark
|
||||||
if (mark === 'delivery') {
|
if (mark === 'delivery') {
|
||||||
optionsCols.value = DELIVERY_TYPES
|
optionsCols.value = DELIVERY_TYPES
|
||||||
pickerRef.value.onOpen([0])
|
pickerRef.value.onOpen([0])
|
||||||
} else if (mark === 'category') {
|
} else if (mark === 'category') {
|
||||||
optionsCols.value = categoryList.value
|
optionsCols.value = categoryList.value
|
||||||
pickerRef.value.onOpen([0, 0])
|
pickerRef.value.onOpen([0, 0])
|
||||||
} else if (mark === 'brand') {
|
} else if (mark === 'brand') {
|
||||||
optionsCols.value = brandList.value
|
optionsCols.value = brandList.value
|
||||||
pickerRef.value.onOpen([0])
|
pickerRef.value.onOpen([0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onRDPickerConfirm(e) {
|
function onRDPickerConfirm(e) {
|
||||||
if (popMark.value === 'delivery') {
|
if (popMark.value === 'delivery') {
|
||||||
formData.value.deliveryTypes = []
|
formData.value.deliveryTypes = []
|
||||||
formData.value.deliveryText = DELIVERY_TYPES[e.value[0]].label
|
formData.value.deliveryText = DELIVERY_TYPES[e.value[0]].label
|
||||||
formData.value.deliveryTypes.push(DELIVERY_TYPES[e.value[0]].value)
|
formData.value.deliveryTypes.push(DELIVERY_TYPES[e.value[0]].value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (popMark.value === 'category') {
|
if (popMark.value === 'category') {
|
||||||
formData.value.categoryId = categoryList.value[e.value[0]].children[e.value[1]].id
|
formData.value.categoryId = categoryList.value[e.value[0]].children[e.value[1]].id
|
||||||
formData.value.categoryText =
|
formData.value.categoryText =
|
||||||
categoryList.value[e.value[0]].name + '/' + categoryList.value[e.value[0]].children[e.value[1]].name
|
categoryList.value[e.value[0]].name + '/' + categoryList.value[e.value[0]].children[e.value[1]].name
|
||||||
}
|
}
|
||||||
|
|
||||||
if (popMark.value === 'brand') {
|
if (popMark.value === 'brand') {
|
||||||
formData.value.brandId = brandList.value[e.value[0]].id
|
formData.value.brandId = brandList.value[e.value[0]].id
|
||||||
}
|
formData.value.brandText = brandList.value[e.value[0]].name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clickSetProperty() {
|
function clickSetProperty() {
|
||||||
// 如果是多规格,处理格式问题,合并属性
|
if (formData.value.skus) {
|
||||||
let temp = formData.value.skus.map((item) => {
|
// 如果是多规格,处理格式问题,合并属性
|
||||||
return item.properties.map((sitem) => ({
|
let temp = formData.value.skus
|
||||||
id: sitem.propertyId,
|
.map((item) => {
|
||||||
children: [sitem.valueId],
|
return item.properties.map((sitem) => ({
|
||||||
}));
|
id: sitem.propertyId,
|
||||||
})
|
children: [sitem.valueId],
|
||||||
.flat(1);
|
}))
|
||||||
|
})
|
||||||
|
.flat(1)
|
||||||
|
|
||||||
// 去除重复数据
|
// 去除重复数据
|
||||||
let result = temp.reduce((pre, cur) => {
|
let result = temp.reduce((pre, cur) => {
|
||||||
let index = pre.findIndex((item) => item.id === cur.id);
|
let index = pre.findIndex((item) => item.id === cur.id)
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
pre[index].children.push(...new Set(cur.children));
|
pre[index].children.push(...new Set(cur.children))
|
||||||
} else {
|
} else {
|
||||||
pre.push(cur);
|
pre.push(cur)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pre
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
peach.$store('trade').$patch({
|
||||||
|
selectedProperty: result,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return pre;
|
peach.$router.go('/pages/product/sku')
|
||||||
}, []);
|
|
||||||
|
|
||||||
peach.$store('trade').$patch({
|
|
||||||
selectedProperty: result,
|
|
||||||
goodsInfo: formData.value,
|
|
||||||
skus: formData.value.skus
|
|
||||||
})
|
|
||||||
peach.$router.go('/pages/product/sku')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProduct(id) {
|
function getProduct(id) {
|
||||||
GoodsApi.getProduct({ id }).then((res) => {
|
GoodsApi.getProduct({ id }).then((res) => {
|
||||||
formData.value = res.data
|
formData.value = res.data
|
||||||
|
|
||||||
// 循环遍历 categoryList,从二级分类找出和 formData.value.categoryId 相等的
|
richValues.value = res.data.description
|
||||||
let tempCategory = categoryList.value.find((item) => {
|
|
||||||
return item.children.find((child) => {
|
// 循环遍历 categoryList,从二级分类找出和 formData.value.categoryId 相等的
|
||||||
return child.id === formData.value.categoryId
|
let tempCategory = categoryList.value.find((item) => {
|
||||||
})
|
return item.children.find((child) => {
|
||||||
|
return child.id === formData.value.categoryId
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
formData.value.categoryText =
|
||||||
|
tempCategory.name +
|
||||||
|
'/' +
|
||||||
|
tempCategory.children.find((item) => {
|
||||||
|
return item.id === formData.value.categoryId
|
||||||
|
}).name
|
||||||
|
|
||||||
|
// 循环遍历 brandList, 从一级分类找出和 formData.value.brandId 相等的
|
||||||
|
let tempBrand = brandList.value.find((item) => {
|
||||||
|
return item.id === formData.value.brandId
|
||||||
|
})
|
||||||
|
|
||||||
|
formData.value.brandText = tempBrand.name
|
||||||
|
|
||||||
|
// 从 DELIVERY_TYPES 找出和 formData.value.deliveryTypes 相等的
|
||||||
|
formData.value.deliveryText = DELIVERY_TYPES.find((item) => {
|
||||||
|
return item.value === formData.value.deliveryTypes[0]
|
||||||
|
}).label
|
||||||
|
|
||||||
|
peach.$store('trade').$patch({
|
||||||
|
goodsInfo: formData.value,
|
||||||
|
skus: formData.value.skus,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
formData.value.categoryText =
|
|
||||||
tempCategory.name +
|
|
||||||
'/' +
|
|
||||||
tempCategory.children.find((item) => {
|
|
||||||
return item.id === formData.value.categoryId
|
|
||||||
}).name
|
|
||||||
|
|
||||||
// 循环遍历 brandList, 从一级分类找出和 formData.value.brandId 相等的
|
|
||||||
let tempBrand = brandList.value.find((item) => {
|
|
||||||
return item.id === formData.value.brandId
|
|
||||||
})
|
|
||||||
|
|
||||||
formData.value.brandText = tempBrand.name
|
|
||||||
|
|
||||||
// 从 DELIVERY_TYPES 找出和 formData.value.deliveryTypes 相等的
|
|
||||||
formData.value.deliveryText = DELIVERY_TYPES.find((item) => {
|
|
||||||
return item.value === formData.value.deliveryTypes[0]
|
|
||||||
}).label
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSubmit() {
|
function onSubmit() {
|
||||||
console.log('res', formData.value)
|
console.log('res', formData.value)
|
||||||
|
console.log('richtext', richValues.value)
|
||||||
|
|
||||||
formRef.value
|
formData.value.description = richValues.value
|
||||||
.validate()
|
formData.value.skus = peach.$store('trade').skus
|
||||||
.then(async (res) => {
|
|
||||||
let tempObj = { ...res }
|
|
||||||
|
|
||||||
// if (formData.value.id) {
|
formRef.value
|
||||||
// tempObj.id = formData.value.id
|
.validate()
|
||||||
// await GoodsApi.editProduct(tempObj)
|
.then(async (res) => {
|
||||||
// } else {
|
// 校验 skus 是否正确填写完成
|
||||||
// await GoodsApi.addProduct(tempObj)
|
validateSku()
|
||||||
// }
|
|
||||||
})
|
let tempObj = _.cloneDeep(formData.value)
|
||||||
.catch((err) => {
|
|
||||||
console.log('err', err)
|
tempObj.skus.forEach((item) => {
|
||||||
})
|
item.name = formData.value.name
|
||||||
|
})
|
||||||
|
|
||||||
|
if (formData.value.id) {
|
||||||
|
tempObj.id = formData.value.id
|
||||||
|
await GoodsApi.editProduct(tempObj)
|
||||||
|
} else {
|
||||||
|
await GoodsApi.addProduct(tempObj)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
uni.showToast({
|
||||||
|
title: err[0].errorMessage,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 4000,
|
||||||
|
})
|
||||||
|
console.log('err', err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得商品分类
|
// 获得商品分类
|
||||||
async function getCategoryList() {
|
async function getCategoryList() {
|
||||||
let { data } = await GoodsApi.getGoodsCategory()
|
let { data } = await GoodsApi.getGoodsCategory()
|
||||||
categoryList.value = handleTree(data)
|
categoryList.value = handleTree(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得商品品牌
|
// 获得商品品牌
|
||||||
async function getBrandList() {
|
async function getBrandList() {
|
||||||
let { data } = await GoodsApi.getBrand()
|
let { data } = await GoodsApi.getBrand()
|
||||||
brandList.value = data
|
brandList.value = data
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoad(async (options) => {
|
onLoad(async (options) => {
|
||||||
await getCategoryList()
|
await getCategoryList()
|
||||||
await getBrandList()
|
await getBrandList()
|
||||||
|
|
||||||
goodsTitle.value = options.title
|
goodsTitle.value = options.title
|
||||||
|
|
||||||
if (options.id) {
|
// 避免页面自动滚动到富文本框,先设置为只读
|
||||||
getProduct(options.id)
|
richReadOnly.value = true
|
||||||
peach.$store('trade').detailTag = options.mark
|
|
||||||
}
|
/**
|
||||||
|
* todo 滚动一定距离后,修改富文本状态和 canEdit 一致
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (options.id) {
|
||||||
|
getProduct(options.id)
|
||||||
|
peach.$store('trade').detailTag = options.mark
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@mixin ss-set-property {
|
@mixin ss-set-property {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: 60rpx;
|
height: 60rpx;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
background: var(--ui-BG-Main);
|
background: var(--ui-BG-Main);
|
||||||
border-radius: 28rpx;
|
border-radius: 28rpx;
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.manage-goods {
|
.manage-goods {
|
||||||
.goods-form {
|
.goods-form {
|
||||||
margin: 40rpx;
|
margin: 40rpx;
|
||||||
|
|
||||||
:deep() {
|
:deep() {
|
||||||
.uni-easyinput__content-input {
|
.uni-easyinput__content-input {
|
||||||
font-size: 28rpx !important;
|
font-size: 28rpx !important;
|
||||||
color: #333333 !important;
|
color: #333333 !important;
|
||||||
line-height: normal !important;
|
line-height: normal !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-easyinput__placeholder-class {
|
.uni-easyinput__placeholder-class {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-direction-left {
|
.is-direction-left {
|
||||||
.uni-forms-item__label {
|
.uni-forms-item__label {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
background-color: #f9f9f9;
|
background-color: #f9f9f9;
|
||||||
border-radius: 10px 0 0 10px;
|
border-radius: 10px 0 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
uni-icons {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-easyinput__content {
|
||||||
|
border-radius: 0 10px 10px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-direction-left {
|
||||||
|
.is-disabled {
|
||||||
|
color: #333333;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-forms-item__error {
|
||||||
|
left: -160rpx !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uni-icons {
|
.btn-group {
|
||||||
margin-right: 10px;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border-radius: 0 10px 10px 0;
|
||||||
|
|
||||||
|
.ss-set-property {
|
||||||
|
@include ss-set-property;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-easyinput__content {
|
.submit-button {
|
||||||
border-radius: 0 10px 10px 0;
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||||
|
color: $white;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.is-direction-left {
|
|
||||||
|
|
||||||
.is-disabled {
|
|
||||||
color: #333333;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uni-forms-item__error {
|
|
||||||
left: -160rpx !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-group {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border-radius: 0 10px 10px 0;
|
|
||||||
|
|
||||||
.ss-set-property {
|
|
||||||
@include ss-set-property
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-button {
|
|
||||||
width: 100%;
|
|
||||||
height: 80rpx;
|
|
||||||
border-radius: 40rpx;
|
|
||||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
|
||||||
color: $white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,45 +1,68 @@
|
||||||
<template>
|
<template>
|
||||||
<pb-layout class="goods-property" title="商品属性" leftIcon="leftIcon" navbar="normal" :bgStyle="bgStyle"
|
<pb-layout
|
||||||
opacityBgUi="bg-white" color="black">
|
class="goods-property"
|
||||||
<view class="property">
|
title="商品属性"
|
||||||
<uni-forms ref="formRef" v-model="formData" :rules="rules" label-position="top" label-width="160">
|
leftIcon="leftIcon"
|
||||||
<uni-forms-item label="商品规格" @tap="pickerProperty" name="specType" label-position="left" required>
|
navbar="normal"
|
||||||
<uni-easyinput type="text" :clearable="false" :styles="selfStyles" placeholderStyle="color:#8a8a8a"
|
:bgStyle="bgStyle"
|
||||||
:inputBorder="false" v-model="formData.specText" placeholder="请选择商品规格" disabled>
|
opacityBgUi="bg-white"
|
||||||
<template v-slot:right>
|
color="black"
|
||||||
<uni-icons type="right" />
|
>
|
||||||
</template>
|
<view class="property">
|
||||||
</uni-easyinput>
|
<uni-forms ref="formRef" v-model="formData" :rules="rules" label-position="top" label-width="160">
|
||||||
</uni-forms-item>
|
<uni-forms-item label="商品规格" @tap="pickerProperty" name="specType" label-position="left" required>
|
||||||
</uni-forms>
|
<uni-easyinput
|
||||||
</view>
|
type="text"
|
||||||
<template v-if="formData.specType">
|
:clearable="false"
|
||||||
<!-- 添加商品 -->
|
:styles="selfStyles"
|
||||||
<button v-if="canEdit" class="ss-reset-button add-property" @tap="showPropertyList">+添加规格</button>
|
placeholderStyle="color:#8a8a8a"
|
||||||
<!-- 商品属性展示 -->
|
:inputBorder="false"
|
||||||
<property-detail v-if="propertyList.length > 0" v-model="propertyList" :goodsPropertyList="goodsPropertyList"
|
v-model="formData.specText"
|
||||||
@changeSubProperty="changeSubProperty"></property-detail>
|
placeholder="请选择商品规格"
|
||||||
<!-- 多规格商品 -->
|
disabled
|
||||||
<mutiple-sku :skus="skus"></mutiple-sku>
|
>
|
||||||
</template>
|
<template v-slot:right>
|
||||||
|
<uni-icons type="right" />
|
||||||
|
</template>
|
||||||
|
</uni-easyinput>
|
||||||
|
</uni-forms-item>
|
||||||
|
</uni-forms>
|
||||||
|
</view>
|
||||||
|
<template v-if="formData.specType">
|
||||||
|
<!-- 添加商品 -->
|
||||||
|
<button v-if="canEdit" class="ss-reset-button add-property" @tap="showPropertyList">+添加规格</button>
|
||||||
|
<!-- 商品属性展示 -->
|
||||||
|
<property-detail
|
||||||
|
v-if="propertyList.length > 0"
|
||||||
|
v-model="propertyList"
|
||||||
|
:goodsPropertyList="goodsPropertyList"
|
||||||
|
@changeSubProperty="changeSubProperty"
|
||||||
|
></property-detail>
|
||||||
|
<!-- 多规格商品 -->
|
||||||
|
<mutiple-sku :skus="skus"></mutiple-sku>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<!-- 单规格商品 -->
|
<!-- 单规格商品 -->
|
||||||
<SkuItem :skus="skus" />
|
<SkuItem :skus="skus" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 确认选择 -->
|
<!-- 确认选择 -->
|
||||||
<view style="padding: 0 40rpx 40rpx;" @tap="onConfirm" v-if="canEdit">
|
<view style="padding: 0 40rpx 40rpx" @tap="submitProperty" v-if="canEdit">
|
||||||
<button class="ss-reset-button submit-button ui-Shadow-Main">提交</button>
|
<button class="ss-reset-button submit-button ui-Shadow-Main">保存</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 商品规格 -->
|
<!-- 商品规格 -->
|
||||||
<p-picker ref="pickerRef" mode="single" :options-cols="SPEC_TYPE" @confirm="onRDPickerConfirm"></p-picker>
|
<p-picker ref="pickerRef" mode="single" :options-cols="SPEC_TYPE" @confirm="onRDPickerConfirm"></p-picker>
|
||||||
|
|
||||||
<!-- 商品属性列表 -->
|
<!-- 商品属性列表 -->
|
||||||
<PropertyList ref="propertyListRef" v-model="propertyList" :goodsPropertyList="goodsPropertyList"
|
<PropertyList
|
||||||
@confirm="onPropertyConfirm" />
|
ref="propertyListRef"
|
||||||
</pb-layout>
|
v-model="propertyList"
|
||||||
|
:goodsPropertyList="goodsPropertyList"
|
||||||
|
@confirm="onPropertyConfirm"
|
||||||
|
/>
|
||||||
|
</pb-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
@ -49,25 +72,26 @@ import PropertyList from './components/propertyList'
|
||||||
import PropertyDetail from './components/propertyDetail'
|
import PropertyDetail from './components/propertyDetail'
|
||||||
import { SPEC_TYPE } from './js/config'
|
import { SPEC_TYPE } from './js/config'
|
||||||
import {
|
import {
|
||||||
initial,
|
initial,
|
||||||
canEdit,
|
canEdit,
|
||||||
pickerRef,
|
pickerRef,
|
||||||
propertyListRef,
|
propertyListRef,
|
||||||
onRDPickerConfirm,
|
onRDPickerConfirm,
|
||||||
formData,
|
formData,
|
||||||
propertyList,
|
propertyList,
|
||||||
onPropertyConfirm,
|
onPropertyConfirm,
|
||||||
showPropertyList,
|
showPropertyList,
|
||||||
goodsPropertyList,
|
goodsPropertyList,
|
||||||
pickerProperty,
|
pickerProperty,
|
||||||
skus,
|
skus,
|
||||||
changeSubProperty,
|
changeSubProperty,
|
||||||
|
submitProperty,
|
||||||
} from './js/sku'
|
} from './js/sku'
|
||||||
|
|
||||||
const bgStyle = {
|
const bgStyle = {
|
||||||
backgroundImage: '',
|
backgroundImage: '',
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
description: '',
|
description: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
initial()
|
initial()
|
||||||
|
@ -75,57 +99,57 @@ initial()
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.goods-property {
|
.goods-property {
|
||||||
.property {
|
.property {
|
||||||
margin: 40rpx;
|
margin: 40rpx;
|
||||||
|
|
||||||
:deep() {
|
:deep() {
|
||||||
.uni-easyinput__content-input {
|
.uni-easyinput__content-input {
|
||||||
font-size: 28rpx !important;
|
font-size: 28rpx !important;
|
||||||
color: #333333 !important;
|
color: #333333 !important;
|
||||||
line-height: normal !important;
|
line-height: normal !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-easyinput__placeholder-class {
|
.uni-easyinput__placeholder-class {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-direction-left {
|
.is-direction-left {
|
||||||
.uni-forms-item__label {
|
.uni-forms-item__label {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
background-color: #f9f9f9;
|
background-color: #f9f9f9;
|
||||||
border-radius: 10px 0 0 10px;
|
border-radius: 10px 0 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
uni-icons {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-easyinput__content {
|
||||||
|
border-radius: 0 10px 10px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-disabled {
|
||||||
|
color: #333333;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uni-icons {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uni-easyinput__content {
|
|
||||||
border-radius: 0 10px 10px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-disabled {
|
|
||||||
color: #333333;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.add-property {
|
.add-property {
|
||||||
margin: 40rpx;
|
margin: 40rpx;
|
||||||
border: 1px dotted var(--ui-BG-Main);
|
border: 1px dotted var(--ui-BG-Main);
|
||||||
color: var(--ui-BG-Main);
|
color: var(--ui-BG-Main);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.submit-button {
|
.submit-button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 80rpx;
|
height: 80rpx;
|
||||||
border-radius: 40rpx;
|
border-radius: 40rpx;
|
||||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||||
color: $white;
|
color: $white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -20,7 +20,7 @@ const GoodsApi = {
|
||||||
// 添加商品
|
// 添加商品
|
||||||
addProduct: (data) => {
|
addProduct: (data) => {
|
||||||
return request({
|
return request({
|
||||||
url: '/trade/order/page',
|
url: '/product/spu/create',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data,
|
data,
|
||||||
})
|
})
|
||||||
|
@ -28,8 +28,8 @@ const GoodsApi = {
|
||||||
// 修改商品
|
// 修改商品
|
||||||
editProduct: (data) => {
|
editProduct: (data) => {
|
||||||
return request({
|
return request({
|
||||||
url: '/trade/order/page',
|
url: '/product/spu/update',
|
||||||
method: 'POST',
|
method: 'PUT',
|
||||||
data,
|
data,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,221 +1,238 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="ss-goods-wrap">
|
<view class="ss-goods-wrap">
|
||||||
<view v-if="size === 'lg'" class="lg-goods-card ss-flex ss-col-stretch" :style="[elStyles]" @click="onClick">
|
<view v-if="size === 'lg'" class="lg-goods-card ss-flex ss-col-stretch" :style="[elStyles]" @click="onClick">
|
||||||
<image class="lg-img-box" :src="peach.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image>
|
<image class="lg-img-box" :src="peach.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image>
|
||||||
<view class="lg-goods-content ss-flex-1 ss-flex-col ss-row-between ss-p-b-10 ss-p-t-20">
|
<view class="lg-goods-content ss-flex-1 ss-flex-col ss-row-between ss-p-b-10 ss-p-t-20">
|
||||||
<view>
|
<view>
|
||||||
<view v-if="goodsFields.title?.show || goodsFields.name?.show" class="lg-goods-title ss-line-2"
|
<view
|
||||||
:style="[{ color: titleColor }]">
|
v-if="goodsFields.title?.show || goodsFields.name?.show"
|
||||||
{{ data.title || data.name }}
|
class="lg-goods-title ss-line-2"
|
||||||
</view>
|
:style="[{ color: titleColor }]"
|
||||||
<view v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
|
>
|
||||||
class="lg-goods-subtitle ss-m-t-10 ss-line-1"
|
{{ data.title || data.name }}
|
||||||
:style="[{ color: subTitleColor, background: subTitleBackground }]">
|
</view>
|
||||||
{{ data.subtitle || data.introduction }}
|
<view
|
||||||
</view>
|
v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
|
||||||
</view>
|
class="lg-goods-subtitle ss-m-t-10 ss-line-1"
|
||||||
<view>
|
:style="[{ color: subTitleColor, background: subTitleBackground }]"
|
||||||
<view class="ss-flex ss-col-bottom ss-m-t-10">
|
>
|
||||||
<view v-if="goodsFields.price?.show" class="lg-goods-price ss-m-r-12 ss-flex ss-col-bottom font-OPPOSANS"
|
{{ data.subtitle || data.introduction }}
|
||||||
:style="[{ color: goodsFields.price.color }]">
|
</view>
|
||||||
<text class="ss-font-24">{{ priceUnit }}</text>
|
</view>
|
||||||
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
<view>
|
||||||
|
<view class="ss-flex ss-col-bottom ss-m-t-10">
|
||||||
|
<view
|
||||||
|
v-if="goodsFields.price?.show"
|
||||||
|
class="lg-goods-price ss-m-r-12 ss-flex ss-col-bottom font-OPPOSANS"
|
||||||
|
:style="[{ color: goodsFields.price.color }]"
|
||||||
|
>
|
||||||
|
<text class="ss-font-24">{{ priceUnit }}</text>
|
||||||
|
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="
|
||||||
|
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
|
||||||
|
(data.original_price > 0 || data.marketPrice > 0)
|
||||||
|
"
|
||||||
|
class="goods-origin-price ss-flex ss-col-bottom font-OPPOSANS"
|
||||||
|
:style="[{ color: originPriceColor }]"
|
||||||
|
>
|
||||||
|
<text class="price-unit ss-font-20">{{ priceUnit }}</text>
|
||||||
|
<view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="ss-m-t-8 ss-flex ss-col-center ss-flex-wrap">
|
||||||
|
<view class="sales-text">{{ salesAndStock }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="
|
|
||||||
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
|
|
||||||
(data.original_price > 0 || data.marketPrice > 0)
|
|
||||||
" class="goods-origin-price ss-flex ss-col-bottom font-OPPOSANS" :style="[{ color: originPriceColor }]">
|
|
||||||
<text class="price-unit ss-font-20">{{ priceUnit }}</text>
|
|
||||||
<view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="ss-m-t-8 ss-flex ss-col-center ss-flex-wrap">
|
|
||||||
<view class="sales-text">{{ salesAndStock }}</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
<view class="ss-flex ss-row-around" :style="btnStyles">
|
||||||
|
<button class="ss-reset-button btn-group" @click="clickGoods('detail')">详情</button>
|
||||||
|
<button class="ss-reset-button btn-group" @click="clickGoods('edit')">编辑</button>
|
||||||
|
<button class="ss-reset-button btn-group btn-del" @click="clickGoods('del')">删除</button>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="ss-flex ss-row-around" :style="btnStyles">
|
|
||||||
<button class="ss-reset-button btn-group" @click="clickGoods('detail')">详情</button>
|
|
||||||
<button class="ss-reset-button btn-group" @click="clickGoods('edit')">编辑</button>
|
|
||||||
<button class="ss-reset-button btn-group btn-del" @click="clickGoods('del')">删除</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import peach from '@/peach'
|
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { isArray } from 'lodash'
|
import { isArray } from 'lodash'
|
||||||
|
import peach from '@/peach'
|
||||||
import { fen2yuan, formatSales, formatStock } from '@/peach/hooks/useGoods'
|
import { fen2yuan, formatSales, formatStock } from '@/peach/hooks/useGoods'
|
||||||
import { unix } from 'dayjs'
|
import { unix } from 'dayjs'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
goodsFields: {
|
goodsFields: {
|
||||||
type: [Array, Object],
|
type: [Array, Object],
|
||||||
default() {
|
default() {
|
||||||
return {
|
return {
|
||||||
price: { show: true },
|
price: { show: true },
|
||||||
stock: { show: true },
|
stock: { show: true },
|
||||||
name: { show: true },
|
name: { show: true },
|
||||||
introduction: { show: true },
|
introduction: { show: true },
|
||||||
marketPrice: { show: true },
|
marketPrice: { show: true },
|
||||||
salesCount: { show: true },
|
salesCount: { show: true },
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: {},
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
originPriceColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#C4C4C4',
|
||||||
|
},
|
||||||
|
topRadius: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
bottomRadius: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
priceUnit: {
|
||||||
|
type: String,
|
||||||
|
default: '¥',
|
||||||
|
},
|
||||||
|
titleColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#333',
|
||||||
|
},
|
||||||
|
subTitleColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#999999',
|
||||||
|
},
|
||||||
|
subTitleBackground: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
},
|
},
|
||||||
},
|
|
||||||
data: {
|
|
||||||
type: Object,
|
|
||||||
default: {},
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
originPriceColor: {
|
|
||||||
type: String,
|
|
||||||
default: '#C4C4C4',
|
|
||||||
},
|
|
||||||
topRadius: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
bottomRadius: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
priceUnit: {
|
|
||||||
type: String,
|
|
||||||
default: '¥',
|
|
||||||
},
|
|
||||||
titleColor: {
|
|
||||||
type: String,
|
|
||||||
default: '#333',
|
|
||||||
},
|
|
||||||
subTitleColor: {
|
|
||||||
type: String,
|
|
||||||
default: '#999999',
|
|
||||||
},
|
|
||||||
subTitleBackground: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const emits = defineEmits(['click'])
|
const emits = defineEmits(['click'])
|
||||||
|
|
||||||
function onClick() {
|
function onClick() {
|
||||||
emits('click')
|
emits('click')
|
||||||
}
|
}
|
||||||
|
|
||||||
const elStyles = computed(() => {
|
const elStyles = computed(() => {
|
||||||
return {
|
return {
|
||||||
background: props.background,
|
background: props.background,
|
||||||
'border-top-left-radius': props.topRadius + 'px',
|
'border-top-left-radius': props.topRadius + 'px',
|
||||||
'border-top-right-radius': props.topRadius + 'px',
|
'border-top-right-radius': props.topRadius + 'px',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const btnStyles = computed(() => {
|
const btnStyles = computed(() => {
|
||||||
return {
|
return {
|
||||||
background: '#fff',
|
background: '#fff',
|
||||||
'border-bottom-left-radius': props.bottomRadius + 'px',
|
'border-bottom-left-radius': props.bottomRadius + 'px',
|
||||||
'border-bottom-right-radius': props.bottomRadius + 'px',
|
'border-bottom-right-radius': props.bottomRadius + 'px',
|
||||||
padding: '8px 0',
|
padding: '8px 0',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 格式化销量、库存信息
|
// 格式化销量、库存信息
|
||||||
const salesAndStock = computed(() => {
|
const salesAndStock = computed(() => {
|
||||||
let text = []
|
let text = []
|
||||||
if (props.goodsFields.salesCount?.show) {
|
if (props.goodsFields.salesCount?.show) {
|
||||||
text.push(formatSales(props.data.sales_show_type, props.data.salesCount))
|
text.push(formatSales(props.data.sales_show_type, props.data.salesCount))
|
||||||
}
|
}
|
||||||
if (props.goodsFields.stock?.show) {
|
if (props.goodsFields.stock?.show) {
|
||||||
text.push(formatStock(props.data.stock_show_type, props.data.stock))
|
text.push(formatStock(props.data.stock_show_type, props.data.stock))
|
||||||
}
|
}
|
||||||
return text.join(' | ')
|
return text.join(' | ')
|
||||||
})
|
})
|
||||||
|
|
||||||
function clickGoods(mark) {
|
function clickGoods(mark) {
|
||||||
|
if (mark === 'detail' || mark === 'edit') {
|
||||||
if (mark === 'detail' || mark === 'edit') {
|
peach.$store('trade').$patch({
|
||||||
peach.$router.go('/pages/product/manageGoods', {
|
selectedProperty: null,
|
||||||
id: props.data.id,
|
goodsInfo: null,
|
||||||
mark: mark,
|
skus: null,
|
||||||
title: mark === 'detail' ? '商品详情' : '编辑商品'
|
})
|
||||||
})
|
peach.$router.go('/pages/product/manageGoods', {
|
||||||
} else if (mark === 'del') {
|
id: props.data.id,
|
||||||
uni.showModal({
|
mark: mark,
|
||||||
title: '提示',
|
title: mark === 'detail' ? '商品详情' : '编辑商品',
|
||||||
content: '是否删除该商品?',
|
})
|
||||||
success: (res) => {
|
} else if (mark === 'del') {
|
||||||
if (res.confirm) {
|
uni.showModal({
|
||||||
}
|
title: '提示',
|
||||||
},
|
content: '是否删除该商品?',
|
||||||
})
|
success: (res) => {
|
||||||
}
|
if (res.confirm) {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.ss-goods-wrap {
|
.ss-goods-wrap {
|
||||||
.lg-goods-card {
|
.lg-goods-card {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
height: 280rpx;
|
height: 280rpx;
|
||||||
|
|
||||||
.lg-img-box {
|
.lg-img-box {
|
||||||
width: 280rpx;
|
width: 280rpx;
|
||||||
height: 280rpx;
|
height: 280rpx;
|
||||||
margin-right: 20rpx;
|
margin-right: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg-goods-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333333;
|
||||||
|
// line-height: 36rpx;
|
||||||
|
// width: 410rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg-goods-subtitle {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #999999;
|
||||||
|
// line-height: 30rpx;
|
||||||
|
// width: 410rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg-goods-price {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: $red;
|
||||||
|
line-height: 36rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sales-text {
|
||||||
|
display: table;
|
||||||
|
font-size: 24rpx;
|
||||||
|
transform: scale(0.8);
|
||||||
|
margin-left: 0rpx;
|
||||||
|
color: #c4c4c4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.lg-goods-title {
|
.btn-group {
|
||||||
font-size: 28rpx;
|
width: 140rpx;
|
||||||
font-weight: 500;
|
height: 55rpx;
|
||||||
color: #333333;
|
line-height: 55rpx;
|
||||||
// line-height: 36rpx;
|
background: var(--ui-BG-1);
|
||||||
// width: 410rpx;
|
border-radius: 25rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lg-goods-subtitle {
|
.btn-del {
|
||||||
font-size: 24rpx;
|
color: var(--ui-BG-Main);
|
||||||
font-weight: 400;
|
background-color: var(--ui-BG-Main-opacity-1);
|
||||||
color: #999999;
|
|
||||||
// line-height: 30rpx;
|
|
||||||
// width: 410rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.lg-goods-price {
|
|
||||||
font-size: 30rpx;
|
|
||||||
color: $red;
|
|
||||||
line-height: 36rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sales-text {
|
|
||||||
display: table;
|
|
||||||
font-size: 24rpx;
|
|
||||||
transform: scale(0.8);
|
|
||||||
margin-left: 0rpx;
|
|
||||||
color: #c4c4c4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-group {
|
|
||||||
width: 120rpx;
|
|
||||||
height: 50rpx;
|
|
||||||
background: var(--ui-BG-1);
|
|
||||||
border-radius: 25rpx;
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-del {
|
|
||||||
color: var(--ui-BG-Main);
|
|
||||||
background-color: var(--ui-BG-Main-opacity-1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,35 +1,47 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="custom-picker">
|
<view class="custom-picker">
|
||||||
<!-- 普通弹窗 -->
|
<!-- 普通弹窗 -->
|
||||||
<uni-popup type="bottom" ref="pickerPopupRef" background-color="#fff">
|
<uni-popup type="bottom" ref="pickerPopupRef" background-color="#fff">
|
||||||
<view class="popup-header">
|
<view class="popup-header">
|
||||||
<view class="button-cancel" @click="onClosePopup">取消</view>
|
<view class="button-cancel" @click="onClosePopup">取消</view>
|
||||||
<view class="button-link" @click="onConfirmPopup">确定</view>
|
<view class="button-link" @click="onConfirmPopup">确定</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="popup-content">
|
<view class="popup-content">
|
||||||
<picker-view :indicator-style="indicatorStyle" :value="pickerViewValue" @change="bindChange"
|
<picker-view
|
||||||
class="picker-view">
|
:indicator-style="indicatorStyle"
|
||||||
<template v-if="mode === 'single'">
|
:value="pickerViewValue"
|
||||||
<picker-view-column>
|
@change="bindChange"
|
||||||
<view class="item" v-for="(item, index) in props.optionsCols" :key="`${item.value}-${index}`">{{
|
class="picker-view"
|
||||||
item.label ?? item.name }}</view>
|
>
|
||||||
</picker-view-column>
|
<template v-if="mode === 'single'">
|
||||||
</template>
|
<picker-view-column>
|
||||||
<template v-else>
|
<view
|
||||||
<picker-view-column>
|
class="item"
|
||||||
<view class="item" v-for="(item, index) in props.optionsCols" :key="`${item.value}-${index}`">{{ item.name
|
v-for="(item, index) in props.optionsCols"
|
||||||
}}</view>
|
:key="`${item.value}-${index}`"
|
||||||
</picker-view-column>
|
>{{ item.label ?? item.name }}</view
|
||||||
<picker-view-column>
|
>
|
||||||
<view class="item" v-for="(item, index) in childrenList" :key="`${item.value}-${index}`">{{
|
</picker-view-column>
|
||||||
item.name
|
</template>
|
||||||
}}</view>
|
<template v-else>
|
||||||
</picker-view-column>
|
<picker-view-column>
|
||||||
</template>
|
<view
|
||||||
</picker-view>
|
class="item"
|
||||||
</view>
|
v-for="(item, index) in props.optionsCols"
|
||||||
</uni-popup>
|
:key="`${item.value}-${index}`"
|
||||||
</view>
|
>{{ item.name }}</view
|
||||||
|
>
|
||||||
|
</picker-view-column>
|
||||||
|
<picker-view-column>
|
||||||
|
<view class="item" v-for="(item, index) in childrenList" :key="`${item.value}-${index}`">{{
|
||||||
|
item.name
|
||||||
|
}}</view>
|
||||||
|
</picker-view-column>
|
||||||
|
</template>
|
||||||
|
</picker-view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
@ -37,20 +49,20 @@ import { defineEmits, defineProps, ref, onMounted, computed, defineExpose } from
|
||||||
const pickerViewValue = ref([])
|
const pickerViewValue = ref([])
|
||||||
const indicatorStyle = `height: 50px`
|
const indicatorStyle = `height: 50px`
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
// 传入pickerview列表 value label字段
|
// 传入pickerview列表 value label字段
|
||||||
optionsCols: {
|
optionsCols: {
|
||||||
default: () => [],
|
default: () => [],
|
||||||
required: true,
|
required: true,
|
||||||
type: Array,
|
type: Array,
|
||||||
},
|
},
|
||||||
mode: {
|
mode: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'single',
|
default: 'single',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const childrenList = computed(() => {
|
const childrenList = computed(() => {
|
||||||
return props.optionsCols[pickerViewValue.value[0]]?.children
|
return props.optionsCols[pickerViewValue.value[0]]?.children
|
||||||
})
|
})
|
||||||
|
|
||||||
// 暴露两个方法confirm change
|
// 暴露两个方法confirm change
|
||||||
|
@ -64,8 +76,8 @@ const pickerPopupRef = ref(null)
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
const onOpen = (defaultValue) => {
|
const onOpen = (defaultValue) => {
|
||||||
pickerViewValue.value = defaultValue
|
pickerViewValue.value = defaultValue
|
||||||
pickerPopupRef.value.open('bottom')
|
pickerPopupRef.value.open('bottom')
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @author: joey wong
|
* @author: joey wong
|
||||||
|
@ -74,17 +86,17 @@ const onOpen = (defaultValue) => {
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
const bindChange = (e) => {
|
const bindChange = (e) => {
|
||||||
pickerViewValue.value = e.detail.value
|
pickerViewValue.value = e.detail.value
|
||||||
|
|
||||||
if (props.mode === 'multiple') {
|
if (props.mode === 'multiple') {
|
||||||
if (pickerViewValue.value[0] !== e.detail.value[0]) {
|
if (pickerViewValue.value[0] !== e.detail.value[0]) {
|
||||||
pickerViewValue.value[1] = 0
|
pickerViewValue.value[1] = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
emit('change', {
|
emit('change', {
|
||||||
value: pickerViewValue.value,
|
value: pickerViewValue.value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @author: joey wong
|
* @author: joey wong
|
||||||
|
@ -92,7 +104,7 @@ const bindChange = (e) => {
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
const onClosePopup = () => {
|
const onClosePopup = () => {
|
||||||
pickerPopupRef.value.close()
|
pickerPopupRef.value.close()
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @author: joey wong
|
* @author: joey wong
|
||||||
|
@ -100,61 +112,61 @@ const onClosePopup = () => {
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
const onConfirmPopup = () => {
|
const onConfirmPopup = () => {
|
||||||
emit('confirm', {
|
emit('confirm', {
|
||||||
value: pickerViewValue.value,
|
value: pickerViewValue.value,
|
||||||
})
|
})
|
||||||
onClosePopup()
|
onClosePopup()
|
||||||
}
|
}
|
||||||
defineExpose({
|
defineExpose({
|
||||||
onOpen,
|
onOpen,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.custom-picker {
|
.custom-picker {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&-text {
|
|
||||||
margin-right: 5.7rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-icon {
|
|
||||||
width: 19rpx;
|
|
||||||
height: 12rpx;
|
|
||||||
margin-bottom: 3rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-content {
|
|
||||||
height: 500rpx;
|
|
||||||
|
|
||||||
.item {
|
|
||||||
text-align: center;
|
|
||||||
line-height: 34px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.picker-view {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin-top: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-link {
|
|
||||||
color: #1892ea;
|
|
||||||
font-size: 28rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-cancel {
|
|
||||||
color: #888;
|
|
||||||
font-size: 28rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-header {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
|
||||||
padding: 24rpx 38rpx 0;
|
&-text {
|
||||||
}
|
margin-right: 5.7rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
width: 19rpx;
|
||||||
|
height: 12rpx;
|
||||||
|
margin-bottom: 3rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-content {
|
||||||
|
height: 500rpx;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
text-align: center;
|
||||||
|
line-height: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-view {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-link {
|
||||||
|
color: #1892ea;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-cancel {
|
||||||
|
color: #888;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 24rpx 38rpx 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
## 1.1.0(2024-04-19)
|
||||||
|
解决不存在问题
|
||||||
|
## 1.0.9(2024-02-04)
|
||||||
|
更新使用文档说明
|
||||||
|
## 1.0.8(2023-12-28)
|
||||||
|
修改文档使用注意事项
|
||||||
|
## 1.0.7(2023-12-05)
|
||||||
|
优化
|
||||||
|
## 1.0.6(2023-12-05)
|
||||||
|
优化
|
|
@ -0,0 +1,784 @@
|
||||||
|
<template>
|
||||||
|
<view v-if="show" class="t-wrapper" @touchmove.stop.prevent="moveHandle">
|
||||||
|
<view class="t-mask" :class="{active:active}" @click.stop="close"></view>
|
||||||
|
<view class="t-box" :class="{active:active}">
|
||||||
|
<view class="t-header">
|
||||||
|
<view class="t-header-button" @click="close">取消</view>
|
||||||
|
<view class="t-header-button" @click="confirm">确认</view>
|
||||||
|
</view>
|
||||||
|
<view class="t-color__box" :style="{ background: 'rgb(' + bgcolor.r + ',' + bgcolor.g + ',' + bgcolor.b + ')'}">
|
||||||
|
<view class="t-background boxs" @touchstart="touchstart($event, 0)" @touchmove="touchmove($event, 0)" @touchend="touchend($event, 0)">
|
||||||
|
<view class="t-color-mask"></view>
|
||||||
|
<view class="t-pointer" :style="{ top: site[0].top - 8 + 'px', left: site[0].left - 8 + 'px' }"></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="t-control__box">
|
||||||
|
<view class="t-control__color">
|
||||||
|
<view class="t-control__color-content" :style="{ background: 'rgba(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ',' + rgba.a + ')' }"></view>
|
||||||
|
</view>
|
||||||
|
<view class="t-control-box__item">
|
||||||
|
<view class="t-controller boxs" @touchstart="touchstart($event, 1)" @touchmove="touchmove($event, 1)" @touchend="touchend($event, 1)">
|
||||||
|
<view class="t-hue">
|
||||||
|
<view class="t-circle" :style="{ left: site[1].left - 12 + 'px' }"></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="t-controller boxs" @touchstart="touchstart($event, 2)" @touchmove="touchmove($event, 2)" @touchend="touchend($event, 2)">
|
||||||
|
<view class="t-transparency">
|
||||||
|
<view class="t-circle" :style="{ left: site[2].left - 12 + 'px' }"></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="t-result__box">
|
||||||
|
<view v-if="mode" class="t-result__item">
|
||||||
|
<view class="t-result__box-input">{{hex}}</view>
|
||||||
|
<view class="t-result__box-text">HEX</view>
|
||||||
|
</view>
|
||||||
|
<template v-else>
|
||||||
|
<view class="t-result__item">
|
||||||
|
<view class="t-result__box-input">{{rgba.r}}</view>
|
||||||
|
<view class="t-result__box-text">R</view>
|
||||||
|
</view>
|
||||||
|
<view class="t-result__item">
|
||||||
|
<view class="t-result__box-input">{{rgba.g}}</view>
|
||||||
|
<view class="t-result__box-text">G</view>
|
||||||
|
</view>
|
||||||
|
<view class="t-result__item">
|
||||||
|
<view class="t-result__box-input">{{rgba.b}}</view>
|
||||||
|
<view class="t-result__box-text">B</view>
|
||||||
|
</view>
|
||||||
|
<view class="t-result__item">
|
||||||
|
<view class="t-result__box-input">{{rgba.a}}</view>
|
||||||
|
<view class="t-result__box-text">A</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<view class="t-result__item t-select" @click="select">
|
||||||
|
<view class="t-result__box-input">
|
||||||
|
<view>切换</view>
|
||||||
|
<view>模式</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="t-alternative">
|
||||||
|
<view class="t-alternative__item" v-for="(item,index) in colorList" :key="index">
|
||||||
|
<view class="t-alternative__item-content" :style="{ background: 'rgba(' + item.r + ',' + item.g + ',' + item.b + ',' + item.a + ')' }"
|
||||||
|
@click="selectColor(item)">
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
color: {
|
||||||
|
type: Object,
|
||||||
|
default () {
|
||||||
|
return {
|
||||||
|
r: 0,
|
||||||
|
g: 0,
|
||||||
|
b: 0,
|
||||||
|
a: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
spareColor: {
|
||||||
|
type: Array,
|
||||||
|
default () {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
show: false,
|
||||||
|
active: false,
|
||||||
|
// rgba 颜色
|
||||||
|
rgba: {
|
||||||
|
r: 0,
|
||||||
|
g: 0,
|
||||||
|
b: 0,
|
||||||
|
a: 1
|
||||||
|
},
|
||||||
|
// hsb 颜色
|
||||||
|
hsb: {
|
||||||
|
h: 0,
|
||||||
|
s: 0,
|
||||||
|
b: 0
|
||||||
|
},
|
||||||
|
site: [{
|
||||||
|
top: 0,
|
||||||
|
left: 0
|
||||||
|
}, {
|
||||||
|
left: 0
|
||||||
|
}, {
|
||||||
|
left: 0
|
||||||
|
}],
|
||||||
|
index: 0,
|
||||||
|
bgcolor: {
|
||||||
|
r: 255,
|
||||||
|
g: 0,
|
||||||
|
b: 0,
|
||||||
|
a: 1
|
||||||
|
},
|
||||||
|
hex: '#000000',
|
||||||
|
mode: true,
|
||||||
|
colorList: [{
|
||||||
|
r: 244,
|
||||||
|
g: 67,
|
||||||
|
b: 54,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 233,
|
||||||
|
g: 30,
|
||||||
|
b: 99,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 156,
|
||||||
|
g: 39,
|
||||||
|
b: 176,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 103,
|
||||||
|
g: 58,
|
||||||
|
b: 183,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 63,
|
||||||
|
g: 81,
|
||||||
|
b: 181,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 33,
|
||||||
|
g: 150,
|
||||||
|
b: 243,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 3,
|
||||||
|
g: 169,
|
||||||
|
b: 244,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 0,
|
||||||
|
g: 188,
|
||||||
|
b: 212,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 0,
|
||||||
|
g: 150,
|
||||||
|
b: 136,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 76,
|
||||||
|
g: 175,
|
||||||
|
b: 80,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 139,
|
||||||
|
g: 195,
|
||||||
|
b: 74,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 205,
|
||||||
|
g: 220,
|
||||||
|
b: 57,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 255,
|
||||||
|
g: 235,
|
||||||
|
b: 59,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 255,
|
||||||
|
g: 193,
|
||||||
|
b: 7,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 255,
|
||||||
|
g: 152,
|
||||||
|
b: 0,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 255,
|
||||||
|
g: 87,
|
||||||
|
b: 34,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 121,
|
||||||
|
g: 85,
|
||||||
|
b: 72,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 158,
|
||||||
|
g: 158,
|
||||||
|
b: 158,
|
||||||
|
a: 1
|
||||||
|
}, {
|
||||||
|
r: 0,
|
||||||
|
g: 0,
|
||||||
|
b: 0,
|
||||||
|
a: 0.5
|
||||||
|
}, {
|
||||||
|
r: 0,
|
||||||
|
g: 0,
|
||||||
|
b: 0,
|
||||||
|
a: 0
|
||||||
|
}, ]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.rgba = this.color;
|
||||||
|
if (this.spareColor.length !== 0) {
|
||||||
|
this.colorList = this.spareColor;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 初始化
|
||||||
|
*/
|
||||||
|
init() {
|
||||||
|
// hsb 颜色
|
||||||
|
this.hsb = this.rgbToHex(this.rgba);
|
||||||
|
// this.setColor();
|
||||||
|
this.setValue(this.rgba);
|
||||||
|
},
|
||||||
|
moveHandle() {},
|
||||||
|
open() {
|
||||||
|
this.show = true;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.init();
|
||||||
|
setTimeout(() => {
|
||||||
|
this.active = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.getSelectorQuery();
|
||||||
|
}, 350)
|
||||||
|
}, 50)
|
||||||
|
})
|
||||||
|
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.active = false;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.show = false;
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
confirm() {
|
||||||
|
this.close();
|
||||||
|
this.$emit('confirm', {
|
||||||
|
rgba: this.rgba,
|
||||||
|
hex: this.hex
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 选择模式
|
||||||
|
select() {
|
||||||
|
this.mode = !this.mode
|
||||||
|
},
|
||||||
|
// 常用颜色选择
|
||||||
|
selectColor(item) {
|
||||||
|
this.setColorBySelect(item)
|
||||||
|
},
|
||||||
|
touchstart(e, index) {
|
||||||
|
const {
|
||||||
|
pageX,
|
||||||
|
pageY
|
||||||
|
} = e.touches[0];
|
||||||
|
this.pageX = pageX;
|
||||||
|
this.pageY = pageY;
|
||||||
|
this.setPosition(pageX, pageY, index);
|
||||||
|
},
|
||||||
|
touchmove(e, index) {
|
||||||
|
const {
|
||||||
|
pageX,
|
||||||
|
pageY
|
||||||
|
} = e.touches[0];
|
||||||
|
this.moveX = pageX;
|
||||||
|
this.moveY = pageY;
|
||||||
|
this.setPosition(pageX, pageY, index);
|
||||||
|
},
|
||||||
|
touchend(e, index) {},
|
||||||
|
/**
|
||||||
|
* 设置位置
|
||||||
|
*/
|
||||||
|
setPosition(x, y, index) {
|
||||||
|
this.index = index;
|
||||||
|
const {
|
||||||
|
top,
|
||||||
|
left,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
} = this.position[index];
|
||||||
|
// 设置最大最小值
|
||||||
|
|
||||||
|
this.site[index].left = Math.max(0, Math.min(parseInt(x - left), width));
|
||||||
|
if (index === 0) {
|
||||||
|
this.site[index].top = Math.max(0, Math.min(parseInt(y - top), height));
|
||||||
|
// 设置颜色
|
||||||
|
this.hsb.s = parseInt((100 * this.site[index].left) / width);
|
||||||
|
this.hsb.b = parseInt(100 - (100 * this.site[index].top) / height);
|
||||||
|
this.setColor();
|
||||||
|
this.setValue(this.rgba);
|
||||||
|
} else {
|
||||||
|
this.setControl(index, this.site[index].left);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 设置 rgb 颜色
|
||||||
|
*/
|
||||||
|
setColor() {
|
||||||
|
const rgb = this.HSBToRGB(this.hsb);
|
||||||
|
this.rgba.r = rgb.r;
|
||||||
|
this.rgba.g = rgb.g;
|
||||||
|
this.rgba.b = rgb.b;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 设置二进制颜色
|
||||||
|
* @param {Object} rgb
|
||||||
|
*/
|
||||||
|
setValue(rgb) {
|
||||||
|
this.hex = '#' + this.rgbToHex(rgb);
|
||||||
|
},
|
||||||
|
setControl(index, x) {
|
||||||
|
const {
|
||||||
|
top,
|
||||||
|
left,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
} = this.position[index];
|
||||||
|
|
||||||
|
if (index === 1) {
|
||||||
|
this.hsb.h = parseInt((360 * x) / width);
|
||||||
|
this.bgcolor = this.HSBToRGB({
|
||||||
|
h: this.hsb.h,
|
||||||
|
s: 100,
|
||||||
|
b: 100
|
||||||
|
});
|
||||||
|
this.setColor()
|
||||||
|
} else {
|
||||||
|
this.rgba.a = (x / width).toFixed(1);
|
||||||
|
}
|
||||||
|
this.setValue(this.rgba);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* rgb 转 二进制 hex
|
||||||
|
* @param {Object} rgb
|
||||||
|
*/
|
||||||
|
rgbToHex(rgb) {
|
||||||
|
let hex = [rgb.r.toString(16), rgb.g.toString(16), rgb.b.toString(16)];
|
||||||
|
hex.map(function(str, i) {
|
||||||
|
if (str.length == 1) {
|
||||||
|
hex[i] = '0' + str;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return hex.join('');
|
||||||
|
},
|
||||||
|
setColorBySelect(getrgb) {
|
||||||
|
const {
|
||||||
|
r,
|
||||||
|
g,
|
||||||
|
b,
|
||||||
|
a
|
||||||
|
} = getrgb;
|
||||||
|
let rgb = {}
|
||||||
|
rgb = {
|
||||||
|
r: r ? parseInt(r) : 0,
|
||||||
|
g: g ? parseInt(g) : 0,
|
||||||
|
b: b ? parseInt(b) : 0,
|
||||||
|
a: a ? a : 0,
|
||||||
|
};
|
||||||
|
this.rgba = rgb;
|
||||||
|
this.hsb = this.rgbToHsb(rgb);
|
||||||
|
this.changeViewByHsb();
|
||||||
|
},
|
||||||
|
changeViewByHsb() {
|
||||||
|
const [a, b, c] = this.position;
|
||||||
|
this.site[0].left = parseInt(this.hsb.s * a.width / 100);
|
||||||
|
this.site[0].top = parseInt((100 - this.hsb.b) * a.height / 100);
|
||||||
|
this.setColor(this.hsb.h);
|
||||||
|
this.setValue(this.rgba);
|
||||||
|
this.bgcolor = this.HSBToRGB({
|
||||||
|
h: this.hsb.h,
|
||||||
|
s: 100,
|
||||||
|
b: 100
|
||||||
|
});
|
||||||
|
|
||||||
|
this.site[1].left = this.hsb.h / 360 * b.width;
|
||||||
|
this.site[2].left = this.rgba.a * c.width;
|
||||||
|
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* hsb 转 rgb
|
||||||
|
* @param {Object} 颜色模式 H(hues)表示色相,S(saturation)表示饱和度,B(brightness)表示亮度
|
||||||
|
*/
|
||||||
|
HSBToRGB(hsb) {
|
||||||
|
let rgb = {};
|
||||||
|
let h = Math.round(hsb.h);
|
||||||
|
let s = Math.round((hsb.s * 255) / 100);
|
||||||
|
let v = Math.round((hsb.b * 255) / 100);
|
||||||
|
if (s == 0) {
|
||||||
|
rgb.r = rgb.g = rgb.b = v;
|
||||||
|
} else {
|
||||||
|
let t1 = v;
|
||||||
|
let t2 = ((255 - s) * v) / 255;
|
||||||
|
let t3 = ((t1 - t2) * (h % 60)) / 60;
|
||||||
|
if (h == 360) h = 0;
|
||||||
|
if (h < 60) {
|
||||||
|
rgb.r = t1;
|
||||||
|
rgb.b = t2;
|
||||||
|
rgb.g = t2 + t3;
|
||||||
|
} else if (h < 120) {
|
||||||
|
rgb.g = t1;
|
||||||
|
rgb.b = t2;
|
||||||
|
rgb.r = t1 - t3;
|
||||||
|
} else if (h < 180) {
|
||||||
|
rgb.g = t1;
|
||||||
|
rgb.r = t2;
|
||||||
|
rgb.b = t2 + t3;
|
||||||
|
} else if (h < 240) {
|
||||||
|
rgb.b = t1;
|
||||||
|
rgb.r = t2;
|
||||||
|
rgb.g = t1 - t3;
|
||||||
|
} else if (h < 300) {
|
||||||
|
rgb.b = t1;
|
||||||
|
rgb.g = t2;
|
||||||
|
rgb.r = t2 + t3;
|
||||||
|
} else if (h < 360) {
|
||||||
|
rgb.r = t1;
|
||||||
|
rgb.g = t2;
|
||||||
|
rgb.b = t1 - t3;
|
||||||
|
} else {
|
||||||
|
rgb.r = 0;
|
||||||
|
rgb.g = 0;
|
||||||
|
rgb.b = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
r: Math.round(rgb.r),
|
||||||
|
g: Math.round(rgb.g),
|
||||||
|
b: Math.round(rgb.b)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
rgbToHsb(rgb) {
|
||||||
|
let hsb = {
|
||||||
|
h: 0,
|
||||||
|
s: 0,
|
||||||
|
b: 0
|
||||||
|
};
|
||||||
|
let min = Math.min(rgb.r, rgb.g, rgb.b);
|
||||||
|
let max = Math.max(rgb.r, rgb.g, rgb.b);
|
||||||
|
let delta = max - min;
|
||||||
|
hsb.b = max;
|
||||||
|
hsb.s = max != 0 ? 255 * delta / max : 0;
|
||||||
|
if (hsb.s != 0) {
|
||||||
|
if (rgb.r == max) hsb.h = (rgb.g - rgb.b) / delta;
|
||||||
|
else if (rgb.g == max) hsb.h = 2 + (rgb.b - rgb.r) / delta;
|
||||||
|
else hsb.h = 4 + (rgb.r - rgb.g) / delta;
|
||||||
|
} else hsb.h = -1;
|
||||||
|
hsb.h *= 60;
|
||||||
|
if (hsb.h < 0) hsb.h = 0;
|
||||||
|
hsb.s *= 100 / 255;
|
||||||
|
hsb.b *= 100 / 255;
|
||||||
|
return hsb;
|
||||||
|
},
|
||||||
|
getSelectorQuery() {
|
||||||
|
const views = uni.createSelectorQuery().in(this);
|
||||||
|
views
|
||||||
|
.selectAll('.boxs')
|
||||||
|
.boundingClientRect(data => {
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
setTimeout(() => this.getSelectorQuery(), 20)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.position = data;
|
||||||
|
// this.site[0].top = data[0].height;
|
||||||
|
// this.site[0].left = 0;
|
||||||
|
// this.site[1].left = data[1].width;
|
||||||
|
// this.site[2].left = data[2].width;
|
||||||
|
this.setColorBySelect(this.rgba);
|
||||||
|
})
|
||||||
|
.exec();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
spareColor(newVal) {
|
||||||
|
this.colorList = newVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.t-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-box {
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
padding: 30upx 0;
|
||||||
|
padding-top: 0;
|
||||||
|
background: #fff;
|
||||||
|
transition: all 0.3s;
|
||||||
|
transform: translateY(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-box.active {
|
||||||
|
transform: translateY(0%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
height: 100upx;
|
||||||
|
border-bottom: 1px #eee solid;
|
||||||
|
box-shadow: 1px 0 2px rgba(0, 0, 0, 0.1);
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-header-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 150upx;
|
||||||
|
height: 100upx;
|
||||||
|
font-size: 30upx;
|
||||||
|
color: #666;
|
||||||
|
padding-left: 20upx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-header-button:last-child {
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-right: 20upx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-mask {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
z-index: -1;
|
||||||
|
transition: all 0.3s;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-mask.active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-color__box {
|
||||||
|
position: relative;
|
||||||
|
height: 400upx;
|
||||||
|
background: rgb(255, 0, 0);
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0 20upx;
|
||||||
|
margin-top: 20upx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-color-mask {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 400upx;
|
||||||
|
background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-pointer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -8px;
|
||||||
|
left: -8px;
|
||||||
|
z-index: 2;
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
border: 1px #fff solid;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-show-color {
|
||||||
|
width: 100upx;
|
||||||
|
height: 50upx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-control__box {
|
||||||
|
margin-top: 50upx;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
padding-left: 20upx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-control__color {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 100upx;
|
||||||
|
height: 100upx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #fff;
|
||||||
|
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||||
|
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||||
|
background-size: 36upx 36upx;
|
||||||
|
background-position: 0 0, 18upx 18upx;
|
||||||
|
border: 1px #eee solid;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-control__color-content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-control-box__item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 30upx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-controller {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 16px;
|
||||||
|
background-color: #fff;
|
||||||
|
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||||
|
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||||
|
background-size: 32upx 32upx;
|
||||||
|
background-position: 0 0, 16upx 16upx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-hue {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-transparency {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgb(0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-circle {
|
||||||
|
position: absolute;
|
||||||
|
/* right: -10px; */
|
||||||
|
top: -2px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-result__box {
|
||||||
|
margin-top: 20upx;
|
||||||
|
padding: 10upx;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-result__item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10upx;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-result__box-input {
|
||||||
|
padding: 10upx 0;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 28upx;
|
||||||
|
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||||
|
color: #999;
|
||||||
|
text-align: center;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-result__box-text {
|
||||||
|
margin-top: 10upx;
|
||||||
|
font-size: 28upx;
|
||||||
|
line-height: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-select {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 150upx;
|
||||||
|
padding: 0 30upx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-select .t-result__box-input {
|
||||||
|
border-radius: 10upx;
|
||||||
|
border: none;
|
||||||
|
color: #999;
|
||||||
|
box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.1);
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-select .t-result__box-input:active {
|
||||||
|
box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-alternative {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
/* justify-content: space-between; */
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 10upx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-alternative__item {
|
||||||
|
margin-left: 12upx;
|
||||||
|
margin-top: 10upx;
|
||||||
|
width: 50upx;
|
||||||
|
height: 50upx;
|
||||||
|
border-radius: 10upx;
|
||||||
|
background-color: #fff;
|
||||||
|
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||||
|
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||||
|
background-size: 36upx 36upx;
|
||||||
|
background-position: 0 0, 18upx 18upx;
|
||||||
|
border: 1px #eee solid;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-alternative__item-content {
|
||||||
|
width: 50upx;
|
||||||
|
height: 50upx;
|
||||||
|
background: rgba(255, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-alternative__item:active {
|
||||||
|
transition: all 0.3s;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,123 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: "iconfont"; /* Project id 4040150 */
|
||||||
|
src: url('iconfont.woff2?t=1682491617906') format('woff2'),
|
||||||
|
url('iconfont.woff?t=1682491617906') format('woff'),
|
||||||
|
url('iconfont.ttf?t=1682491617906') format('truetype');
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconfont {
|
||||||
|
font-family: "iconfont" !important;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-checklist:before {
|
||||||
|
content: "\e600";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zitiyanse:before {
|
||||||
|
content: "\e646";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-formatheader1:before {
|
||||||
|
content: "\e860";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-formatheader2:before {
|
||||||
|
content: "\e861";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-undo:before {
|
||||||
|
content: "\e787";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-redo:before {
|
||||||
|
content: "\e788";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-indent:before {
|
||||||
|
content: "\e7f3";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-outdent:before {
|
||||||
|
content: "\e7f4";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zitijiacu:before {
|
||||||
|
content: "\ec83";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zuoyouduiqi:before {
|
||||||
|
content: "\ec87";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-Character-Spacing:before {
|
||||||
|
content: "\ed91";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-format:before {
|
||||||
|
content: "\e6da";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-font-size:before {
|
||||||
|
content: "\e7b9";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-duanhouju:before {
|
||||||
|
content: "\e61a";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-duanqianju:before {
|
||||||
|
content: "\e61b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-shanchuxian:before {
|
||||||
|
content: "\e602";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-charutupian:before {
|
||||||
|
content: "\e603";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-fengexian:before {
|
||||||
|
content: "\e60e";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-juzhongduiqi:before {
|
||||||
|
content: "\e620";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-wuxupailie:before {
|
||||||
|
content: "\e63e";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-youduiqi:before {
|
||||||
|
content: "\e64b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-youxupailie:before {
|
||||||
|
content: "\e64c";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zitixiahuaxian:before {
|
||||||
|
content: "\e657";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zitixieti:before {
|
||||||
|
content: "\e658";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zuoduiqi:before {
|
||||||
|
content: "\e65a";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-LineHeight:before {
|
||||||
|
content: "\e624";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-editor-background-color:before {
|
||||||
|
content: "\e829";
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,516 @@
|
||||||
|
<template>
|
||||||
|
<view class="container-editor">
|
||||||
|
<view class="textarea">
|
||||||
|
<view class="page-body">
|
||||||
|
<view class="wrapper">
|
||||||
|
<PickerColor
|
||||||
|
ref="colorPicker"
|
||||||
|
:color="{ r: 255, g: 0, b: 0, a: 0.6 }"
|
||||||
|
@confirm="confirm"
|
||||||
|
></PickerColor>
|
||||||
|
<view class="toolbar" @tap="format">
|
||||||
|
<!-- <view
|
||||||
|
:class="formats.fontSize === '24px' ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-font-size"
|
||||||
|
data-name="fontSize"
|
||||||
|
data-value="24px"
|
||||||
|
></view>
|
||||||
|
<view
|
||||||
|
:class="formats.color ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-zitiyanse"
|
||||||
|
data-name="color"
|
||||||
|
:data-value="formats.color"
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
:class="formats.header === 1 ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-formatheader1"
|
||||||
|
data-name="header"
|
||||||
|
:data-value="1"
|
||||||
|
></view>
|
||||||
|
<view
|
||||||
|
:class="formats.header === 2 ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-formatheader2"
|
||||||
|
data-name="header"
|
||||||
|
:data-value="2"
|
||||||
|
></view> -->
|
||||||
|
<view :class="formats.bold ? 'ql-active' : ''" class="iconfont icon-zitijiacu" data-name="bold">
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
:class="formats.italic ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-zitixieti"
|
||||||
|
data-name="italic"
|
||||||
|
></view>
|
||||||
|
<view
|
||||||
|
:class="formats.underline ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-zitixiahuaxian"
|
||||||
|
data-name="underline"
|
||||||
|
></view>
|
||||||
|
<view
|
||||||
|
:class="formats.strike ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-shanchuxian"
|
||||||
|
data-name="strike"
|
||||||
|
></view>
|
||||||
|
<view
|
||||||
|
:class="formats.align === 'left' ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-zuoduiqi"
|
||||||
|
data-name="align"
|
||||||
|
data-value="left"
|
||||||
|
></view>
|
||||||
|
<view
|
||||||
|
:class="formats.align === 'center' ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-juzhongduiqi"
|
||||||
|
data-name="align"
|
||||||
|
data-value="center"
|
||||||
|
></view>
|
||||||
|
<view
|
||||||
|
:class="formats.align === 'right' ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-youduiqi"
|
||||||
|
data-name="align"
|
||||||
|
data-value="right"
|
||||||
|
></view>
|
||||||
|
<view
|
||||||
|
:class="formats.align === 'justify' ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-zuoyouduiqi"
|
||||||
|
data-name="align"
|
||||||
|
data-value="justify"
|
||||||
|
></view>
|
||||||
|
<!-- <view
|
||||||
|
:class="formats.lineHeight ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-LineHeight"
|
||||||
|
data-name="lineHeight"
|
||||||
|
data-value="2"
|
||||||
|
></view>
|
||||||
|
<view
|
||||||
|
:class="formats.letterSpacing ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-Character-Spacing"
|
||||||
|
data-name="letterSpacing"
|
||||||
|
data-value="2em"
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
:class="formats.marginTop ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-duanqianju"
|
||||||
|
data-name="marginTop"
|
||||||
|
data-value="10px"
|
||||||
|
></view>
|
||||||
|
<view
|
||||||
|
:class="formats.previewarginBottom ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-duanhouju"
|
||||||
|
data-name="marginBottom"
|
||||||
|
data-value="10px"
|
||||||
|
></view> -->
|
||||||
|
<!-- <view class="iconfont icon-rili4" @tap="insertDate"></view> -->
|
||||||
|
<!-- <view class="iconfont icon-checklist" data-name="list" data-value="check"></view> -->
|
||||||
|
<view
|
||||||
|
:class="formats.list === 'ordered' ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-youxupailie"
|
||||||
|
data-name="list"
|
||||||
|
data-value="ordered"
|
||||||
|
></view>
|
||||||
|
<view
|
||||||
|
:class="formats.list === 'bullet' ? 'ql-active' : ''"
|
||||||
|
class="iconfont icon-wuxupailie"
|
||||||
|
data-name="list"
|
||||||
|
data-value="bullet"
|
||||||
|
></view>
|
||||||
|
<!-- <view class="iconfont icon-outdent" data-name="indent" data-value="-1"></view>
|
||||||
|
<view class="iconfont icon-indent" data-name="indent" data-value="+1"></view> -->
|
||||||
|
<view class="iconfont icon-fengexian" @tap="insertDivider"></view>
|
||||||
|
<view class="iconfont icon-charutupian" @tap="insertImage"></view>
|
||||||
|
<view class="iconfont icon-undo" @tap="undo"></view>
|
||||||
|
<view class="iconfont icon-redo" @tap="redo"></view>
|
||||||
|
<view class="iconfont icon-format" @tap="clear"></view>
|
||||||
|
</view>
|
||||||
|
<editor
|
||||||
|
id="editor"
|
||||||
|
class="editor"
|
||||||
|
placeholder="开始输入..."
|
||||||
|
showImgSize
|
||||||
|
showImgToolbar
|
||||||
|
showImgResize
|
||||||
|
@statuschange="onStatusChange"
|
||||||
|
:read-only="readOnly"
|
||||||
|
@ready="onEditorReady"
|
||||||
|
@input="saveContens"
|
||||||
|
>
|
||||||
|
</editor>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PickerColor from './color-picker.vue'
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
PickerColor,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
api: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
photoUrl: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
values: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
readOnly: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
maxlength: {
|
||||||
|
type: Number,
|
||||||
|
default: 300,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: 'file',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentTab: 0,
|
||||||
|
curColor: '#000000',
|
||||||
|
show: true,
|
||||||
|
hdid: '',
|
||||||
|
myHtml: '',
|
||||||
|
formats: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showPicker() {
|
||||||
|
this.$refs.colorPicker.open()
|
||||||
|
},
|
||||||
|
confirm(e) {
|
||||||
|
this.editorCtx.format('color', e.hex)
|
||||||
|
},
|
||||||
|
saveContens() {
|
||||||
|
let that = this
|
||||||
|
let maxlength = parseInt(that.maxlength)
|
||||||
|
that.editorCtx.getContents({
|
||||||
|
success: function (res) {
|
||||||
|
let html_text = res.html
|
||||||
|
let html_length = html_text.length
|
||||||
|
if (html_length > maxlength) {
|
||||||
|
uni.showModal({
|
||||||
|
title: '最多只能输入' + maxlength + '字',
|
||||||
|
confirmText: '确定',
|
||||||
|
showCancel: false,
|
||||||
|
success(res) {
|
||||||
|
that.$emit('changes', {
|
||||||
|
html: res.html,
|
||||||
|
length: html_length,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
that.$emit('changes', {
|
||||||
|
html: res.html,
|
||||||
|
length: html_length,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
update() {
|
||||||
|
//获取一下是否有数据
|
||||||
|
let that = this
|
||||||
|
setTimeout(() => {
|
||||||
|
that.editorCtx.setContents({
|
||||||
|
html: that.values,
|
||||||
|
})
|
||||||
|
}, 1000)
|
||||||
|
},
|
||||||
|
onEditorReady() {
|
||||||
|
let that = this
|
||||||
|
console.log(uni.createSelectorQuery().in(this).select('#editor'))
|
||||||
|
uni.createSelectorQuery()
|
||||||
|
.in(this)
|
||||||
|
.select('#editor')
|
||||||
|
.context((res) => {
|
||||||
|
that.editorCtx = res.context
|
||||||
|
that.update()
|
||||||
|
})
|
||||||
|
.exec((result) => {})
|
||||||
|
},
|
||||||
|
undo() {
|
||||||
|
this.editorCtx.undo()
|
||||||
|
},
|
||||||
|
redo() {
|
||||||
|
this.editorCtx.redo()
|
||||||
|
},
|
||||||
|
format(e) {
|
||||||
|
let { name, value } = e.target.dataset
|
||||||
|
if (!name) return
|
||||||
|
if (name == 'color') {
|
||||||
|
this.showPicker()
|
||||||
|
} else {
|
||||||
|
this.editorCtx.format(name, value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onStatusChange(e) {
|
||||||
|
const formats = e.detail
|
||||||
|
this.formats = formats
|
||||||
|
},
|
||||||
|
insertDivider() {
|
||||||
|
this.editorCtx.insertDivider()
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
this.editorCtx.clear()
|
||||||
|
this.$emit()
|
||||||
|
},
|
||||||
|
insertDate() {
|
||||||
|
const date = new Date()
|
||||||
|
const formatDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`
|
||||||
|
this.editorCtx.insertText({
|
||||||
|
text: formatDate,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
insertImage() {
|
||||||
|
let that = this
|
||||||
|
// #ifdef APP-PLUS || H5
|
||||||
|
uni.chooseImage({
|
||||||
|
count: 1, //默认9
|
||||||
|
sizeType: ['original'], //可以指定是原图还是压缩图,默认二者都有
|
||||||
|
sourceType: ['album'], //从相册或者相机选择
|
||||||
|
success: (res) => {
|
||||||
|
const tempFilePaths = res.tempFilePaths[0]
|
||||||
|
if (!this.api || !this.photoUrl) {
|
||||||
|
that.editorCtx.insertImage({
|
||||||
|
src: tempFilePaths,
|
||||||
|
alt: '图像',
|
||||||
|
success: function () {},
|
||||||
|
})
|
||||||
|
uni.showToast({
|
||||||
|
title: '未传入api字段或者photoUrl字段,此为临时图片路径',
|
||||||
|
duration: 3000,
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.uploadFile({
|
||||||
|
url: this.photoUrl + this.api,
|
||||||
|
filePath: tempFilePaths,
|
||||||
|
name: this.name,
|
||||||
|
header: {
|
||||||
|
// Accept: 'text/json',
|
||||||
|
Accept: '*/*',
|
||||||
|
'tenant-id': '1',
|
||||||
|
// Authorization: 'Bearer test247',
|
||||||
|
},
|
||||||
|
formData: {},
|
||||||
|
success: (uploadFileRes) => {
|
||||||
|
let result = JSON.parse(uploadFileRes.data)
|
||||||
|
|
||||||
|
if (result.error === 1) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: result.msg,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '上传成功',
|
||||||
|
})
|
||||||
|
|
||||||
|
this.editorCtx.insertImage({
|
||||||
|
src: result.data,
|
||||||
|
alt: '图像',
|
||||||
|
success: function () {},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail() {
|
||||||
|
uni.showToast({
|
||||||
|
title: '未授权访问相册权限,请授权后使用',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
// #ifdef MP
|
||||||
|
uni.chooseMedia({
|
||||||
|
count: 1,
|
||||||
|
mediaType: ['image'],
|
||||||
|
sourceType: ['album'],
|
||||||
|
sizeType: 'compressed',
|
||||||
|
success: (chooseImageRes) => {
|
||||||
|
const tempFilePaths = chooseImageRes.tempFiles[0].tempFilePath
|
||||||
|
console.log(tempFilePaths)
|
||||||
|
if (!this.api || !this.photoUrl) {
|
||||||
|
that.editorCtx.insertImage({
|
||||||
|
src: tempFilePaths,
|
||||||
|
alt: '图像',
|
||||||
|
success: function () {},
|
||||||
|
})
|
||||||
|
uni.showToast({
|
||||||
|
title: '未传入api字段或者photoUrl字段,此为临时图片路径',
|
||||||
|
duration: 3000,
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.uploadFile({
|
||||||
|
url: this.photoUrl + this.api,
|
||||||
|
filePath: tempFilePaths,
|
||||||
|
name: this.name,
|
||||||
|
header: {
|
||||||
|
// Accept: 'text/json',
|
||||||
|
Accept: '*/*',
|
||||||
|
'tenant-id': '1',
|
||||||
|
// Authorization: 'Bearer test247',
|
||||||
|
},
|
||||||
|
formData: {},
|
||||||
|
success: (uploadFileRes) => {
|
||||||
|
let result = JSON.parse(uploadFileRes.data)
|
||||||
|
|
||||||
|
if (result.error === 1) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: result.msg,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '上传成功',
|
||||||
|
})
|
||||||
|
|
||||||
|
this.editorCtx.insertImage({
|
||||||
|
src: result.data,
|
||||||
|
alt: '图像',
|
||||||
|
success: function () {},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail(err) {
|
||||||
|
console.log(err)
|
||||||
|
uni.showToast({
|
||||||
|
title: err.errMsg,
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@import url('iconfont.css');
|
||||||
|
|
||||||
|
.container-editor {
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs .current {
|
||||||
|
border-bottom: 2px solid #0369d6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs .tab {
|
||||||
|
font-size: 32upx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
padding: 20upx;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
line-height: 80upx;
|
||||||
|
border-bottom: 1px solid #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .item .left {
|
||||||
|
min-width: 200upx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .title {
|
||||||
|
padding-bottom: 20upx;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 1px solid #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .textarea {
|
||||||
|
border: 1px solid #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
padding: 0 10upx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconfont {
|
||||||
|
display: inline-block;
|
||||||
|
width: 9%;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 40upx !important;
|
||||||
|
text-align: center;
|
||||||
|
padding: 10upx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-rili4 {
|
||||||
|
font-size: 48upx !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-duanqianju,
|
||||||
|
.icon-duanhouju,
|
||||||
|
.icon-zitijiacu,
|
||||||
|
.icon-zitixieti,
|
||||||
|
.icon-zitixiahuaxian,
|
||||||
|
.icon-shanchuxian {
|
||||||
|
font-size: 36upx !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-bottom: 0;
|
||||||
|
margin-bottom: 10upx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-container {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 300upx;
|
||||||
|
height: auto;
|
||||||
|
background: #fff;
|
||||||
|
font-size: 32upx;
|
||||||
|
line-height: 1;
|
||||||
|
padding-bottom: 60upx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ .ql-editor.ql-blank:before {
|
||||||
|
font-size: 28upx;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-active {
|
||||||
|
color: #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 150upx;
|
||||||
|
font-size: 30upx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"id": "piaoyi-editor",
|
||||||
|
"name": "多功能富文本编辑器",
|
||||||
|
"displayName": "多功能富文本编辑器",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"description": "富文本编辑器,内置上传图片以及更改颜色, 多样化等功能",
|
||||||
|
"keywords": [
|
||||||
|
"富文本编辑器",
|
||||||
|
"上传图片",
|
||||||
|
"字体颜色"
|
||||||
|
],
|
||||||
|
"dcloudext": {
|
||||||
|
"type": "component-vue"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
### piaoyiEditor 富文本编辑器
|
||||||
|
|
||||||
|
**使用方法:**
|
||||||
|
|
||||||
|
```
|
||||||
|
<template>
|
||||||
|
<view class="richtext">
|
||||||
|
<piaoyiEditor :values="values" :maxlength="3000" @changes="saveContens" :readOnly="readOnly" :photoUrl="photoUrl" :api="api" :name="name"/>
|
||||||
|
<view class="">
|
||||||
|
{{txt}}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import piaoyiEditor from '@/uni_modules/piaoyi-editor/components/piaoyi-editor/piaoyi-editor.vue';
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
readOnly: false, //是否只读
|
||||||
|
photoUrl: 'http://test.com', //服务器图片域名或者ip
|
||||||
|
api: '/upload', //上传图片接口地址
|
||||||
|
txt: '',
|
||||||
|
name: 'file',
|
||||||
|
values: '<div>11111222</div>'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
piaoyiEditor
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
saveContens(e) {
|
||||||
|
this.txt = e.html
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onShareAppMessage(res) {
|
||||||
|
if (res.from === 'button') { // 来自页面内分享按钮
|
||||||
|
console.log(res.target)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
title: '多功能富文本编辑器!',
|
||||||
|
path: '/pages/editor/editor'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onShareTimeline(res) {
|
||||||
|
if (res.from === 'button') { // 来自页面内分享按钮
|
||||||
|
console.log(res.target)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
title: '多功能富文本编辑器!'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 事件说明
|
||||||
|
|
||||||
|
1、**使用上传图片功能时需要注意查看代码里面的uni.uploadFile方法,因为它的返回值取决于自己后端接口的值,所以可以根据实际情况就行更改即可**
|
||||||
|
|
||||||
|
| 事件名 | 返回值 | 描述 |
|
||||||
|
| :---------: | :----: | :------------: |
|
||||||
|
| @saveContens | {html: html片段, length: html长度} | 文本框内容回调 |
|
||||||
|
|
||||||
|
#### Prop
|
||||||
|
|
||||||
|
| 参数名称 | 默认值 | 描述 |
|
||||||
|
| -------- | ------------------------------ |
|
||||||
|
| maxlength| 300 | 输入最大长度 |
|
||||||
|
| readOnly | false | 是否只读 |
|
||||||
|
| api | 空 | 上传图片接口地址 |
|
||||||
|
| photoUrl | 空 | 服务器图片域名或者ip |
|
||||||
|
| name | 'file' | 上传图片接口的key |
|
||||||
|
| values | '' | 富文本编辑器默认值 |
|
||||||
|
|
||||||
|
### 注:近期收到使用用户反馈,存在以下四个问题(如有好的建议,期待私信,谢谢)
|
||||||
|
|
||||||
|
1、当组件在页面中部或者底部的时候,进入页面,页面会自动滚动到富文本编辑器的区域
|
||||||
|
属于正常现象;
|
||||||
|
官网文档有这么一句话:编辑器聚焦时页面会被上推,系统行为以保证编辑区可见;
|
||||||
|
作者建议这种情况,进入页面初始设置富文本编辑器为只读,然后页面滚动到一定距离的时候取消这个只读;
|
||||||
|
|
||||||
|
2、组件粘贴文字出现软键盘闪烁,导致文字粘贴不了
|
||||||
|
目前暂未发现解决方法,本插件是在官方的editor基础上开发的,这个组件存在这个问题;
|
||||||
|
经测试长按出现粘贴后,手不松开滑动到粘贴字样上就不会出现闪烁,然后松开手,点击粘贴就可以;
|
||||||
|
|
||||||
|
3、有些上传图片接口是需要token的,接口需要token的话可以在组件内搜索uni.uploadFile,加上headers头部参数
|
||||||
|
|
||||||
|
4、H5有时候会出现插件异常情况,不要慌,查看[editor组件官网](https://uniapp.dcloud.net.cn/component/editor.html)官网下的注意事项(原话是:H5 端需要动态引入 quill.min.js、image-resize.min.js 依赖,默认情况下浏览器会从 unpkg.com 加载。如果依赖加载较慢或失败,uni-app 建议使用通过测试的 js 依赖保证效果一致,访问 github.com 或者 gitee.com 选择下载。可以放入 static 目录进行托管,或者使用 CDN 服务商。为了保证服务的稳定性,推荐开发者将所有前端资源使用 uniCloud 前端网页托管 服务进行托管,然后在 自定义模板 的 head 标签内引入相关 js 依赖。)
|
||||||
|
|
||||||
|
(1)上面H5这种情况我推荐一种方法进行测试:把所需要的静态资源下载到本地,在APP.vue里面的onLunch下进行dom操作插入这些静态资源
|
||||||
|
(2)使用官方提供的自定义模板(hx新建项目的Hello uni-app模板里面有示例):使用官方的自定义模板引入静态资源,亲测引入静态资源成功了(即引入js和css都是本地的静态资源,不再请求远程CDN文件),但是原来页面样式全丢失了,目前还没有找到问题所在没有成功的朋友欢迎加群交流一下,谢谢
|
||||||
|
|
||||||
|
### 可接定制化组件开发
|
||||||
|
### 右侧有本人代表作小程序二维码,可以扫码体验
|
||||||
|
### 如使用过程中有问题或有一些好的建议,欢迎加QQ群互相学习交流:120594820
|
Loading…
Reference in New Issue