This commit is contained in:
zhangxingye 2025-02-22 17:51:09 +08:00
parent 998bc45df8
commit dd8b697d12
77 changed files with 5726 additions and 392 deletions

View File

@ -0,0 +1,59 @@
<template>
<view :style="themeColor()">
<view v-show="!loading" class="diy-template-wrap">
<diy-group ref="diyGroupRef" :data="diyFormData" />
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted } from 'vue';
import diyGroup from '@/addon/components/diy/group/index.vue'
import { getFormRecord } from '@/app/api/diy_form';
const props = defineProps(['record_id','completeLayout']);
const emits = defineEmits(['callback'])
const loading = ref(true);
const diyFormData: any = reactive({
global: {},
value: []
})
onMounted(() => {
getFormRecord({
record_id: props.record_id
}).then((res: any) => {
diyFormData.global.completeLayout = props.completeLayout || 'style-1';
if (res.data.recordsFieldList) {
res.data.recordsFieldList.forEach((item: any) => {
let comp = {
id: item.field_key,
componentName: item.field_type,
pageStyle: '',
viewFormDetail: true, //
field: {
name: item.field_name,
value: item.handle_field_value,
required: item.field_required,
unique: item.field_unique,
privacyProtection: item.privacy_protection,
},
margin: {
top: 0,
bottom: 0,
both: 0
}
};
diyFormData.value.push(comp);
})
}
emits('callback', res.data.recordsFieldList)
loading.value = false;
}).catch(() => {
loading.value = false;
emits('callback', [])
})
})
</script>

View File

@ -0,0 +1,191 @@
<template>
<view :style="themeColor()">
<!-- 自定义组件渲染 -->
<view v-show="requestData.status == 1 && !diy.getLoading()" class="diy-template-wrap">
<diy-group ref="diyGroupRef" :data="diyFormData" />
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, onMounted, watch } from 'vue';
import { useDiyForm } from '@/hooks/useDiyForm'
import { deepClone,getValidTime } from '@/utils/common'
import diyGroup from '@/addon/components/diy/group/index.vue'
const props = defineProps(['form_id', 'relate_id', 'storage_name', 'form_border']);
const diy = useDiyForm({
form_id: props.form_id,
needLogin: false //
})
const diyGroupRef = ref(null)
const requestData = computed(() => {
return diy.requestData;
})
const diyFormData: any = reactive({})
onMounted(() => {
diy.getData(() => {
diyFormData.status = diy.data.status;
if (diyFormData.status) {
diyFormData.title = diy.data.title;
diyFormData.global = diy.data.global;
if (diyFormData.global) {
diyFormData.global.topStatusBar.isShow = false; //
diyFormData.global.bottomTabBarSwitch = false; //
}
let value: any = [];
if(props.form_border == 'none'){
diyFormData.global.borderControl = false;
}
//
diy.data.value.forEach((item: any) => {
if (item.componentType == 'diy_form' && item.componentName != 'FormSubmit') {
value.push(item);
}
})
diyFormData.value = value;
diyFormData.componentRefs = null;
diyGroupRef.value?.refresh();
watchFormData();
}
})
})
const watchFormData = () => {
watch(
() => diyFormData.value,
(newValue, oldValue) => {
if (newValue) {
let formData: any = {
validTime: getValidTime(5), // 5
components: []
};
newValue.forEach((item: any) => {
//
if (item.componentType == 'diy_form' && item.componentName != 'FormSubmit') {
//
let field = deepClone(item.field);
//
delete field.remark; //
delete field.detailComponent; //
delete field.default; //
formData.components.push({
id: item.id,
componentName: item.componentName,
componentType: item.componentType,
componentTitle: item.componentTitle,
isHidden: item.isHidden,
field: field
})
}
})
if (formData.components.length) {
uni.setStorageSync('diyFormStorage_' + props.form_id, formData)
}
}
},
{ deep: true }
)
}
const verify = () => {
if(!diyFormData.status) return true;
if(!diyFormData.value) return true;
let allPass = true; //
let componentRefs = diyGroupRef.value.getFormRef().componentRefs;
//
for (let i = 0; i < diyFormData.value.length; i++) {
let item = diyFormData.value[i];
if (item.field.required || item.field.value) {
let refKey = `diy${ item.componentName }Ref`;
let isBreak = false;
if (componentRefs[refKey]) {
for (let k = 0; k < componentRefs[refKey].length; k++) {
let compRef = componentRefs[refKey][k];
let verify = compRef.verify(); //
if (verify && !verify.code) {
isBreak = true;
uni.showToast({
title: verify.message,
icon: 'none'
});
break;
}
}
if (isBreak) {
allPass = false;
break;
}
}
}
}
if (!allPass) return false;
const data = {
form_id: props.form_id,
value: uni.getStorageSync('diyFormStorage_' + props.form_id),
relate_id: props.relate_id || 0 // id
}
if (props.storage_name) {
uni.setStorageSync(props.storage_name, data)
}
return allPass;
}
//
const getData = ()=> {
return {
form_id: props.form_id,
value: diyFormData.value,
relate_id: props.relate_id || 0 // id
}
}
const clearStorage = (keys: any=[]) => {
uni.removeStorageSync('diyFormStorage_' + props.form_id)
if (props.storage_name) uni.removeStorageSync(props.storage_name)
if(keys) {
keys.forEach((key: any) => {
uni.removeStorageSync(key)
})
}
}
//
diy.onHide();
//
diy.onUnload();
//
// diy.onPageScroll()
defineExpose({
verify,
getData,
clearStorage
})
</script>
<style lang="scss">
.diy-template-wrap {
/* #ifdef MP */
.child-diy-template-wrap {
::v-deep .diy-group {
> .draggable-element.top-fixed-diy {
display: block !important;
}
}
}
/* #endif */
}
</style>

View File

@ -8,53 +8,114 @@
<view v-if="diyGroup.isShowPlaceHolder(index,component)" class="absolute w-full z-1" :style="{ height : (component.margin.top * 2 * -1) + 'rpx' }" @click.stop="diyGroup.placeholderEvent"></view> <view v-if="diyGroup.isShowPlaceHolder(index,component)" class="absolute w-full z-1" :style="{ height : (component.margin.top * 2 * -1) + 'rpx' }" @click.stop="diyGroup.placeholderEvent"></view>
<template v-if="component.componentName == 'GraphicNav'"> <template v-if="component.componentName == 'GraphicNav'">
<diy-graphic-nav :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount" /> <diy-graphic-nav :component="component" :global="data.global" :index="index" />
</template> </template>
<template v-if="component.componentName == 'HorzBlank'"> <template v-if="component.componentName == 'HorzBlank'">
<diy-horz-blank :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount" /> <diy-horz-blank :component="component" :global="data.global" :index="index" />
</template> </template>
<template v-if="component.componentName == 'HorzLine'"> <template v-if="component.componentName == 'HorzLine'">
<diy-horz-line :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount" /> <diy-horz-line :component="component" :global="data.global" :index="index" />
</template> </template>
<template v-if="component.componentName == 'HotArea'"> <template v-if="component.componentName == 'HotArea'">
<diy-hot-area :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount" /> <diy-hot-area :component="component" :global="data.global" :index="index" />
</template> </template>
<template v-if="component.componentName == 'ImageAds'"> <template v-if="component.componentName == 'ImageAds'">
<diy-image-ads :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount" /> <diy-image-ads :component="component" :global="data.global" :index="index" />
</template> </template>
<template v-if="component.componentName == 'MemberInfo'"> <template v-if="component.componentName == 'MemberInfo'">
<diy-member-info :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount" /> <diy-member-info :component="component" :global="data.global" :index="index" />
</template> </template>
<template v-if="component.componentName == 'MemberLevel'"> <template v-if="component.componentName == 'MemberLevel'">
<diy-member-level :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount" /> <diy-member-level :component="component" :global="data.global" :index="index" />
</template> </template>
<template v-if="component.componentName == 'Notice'"> <template v-if="component.componentName == 'Notice'">
<diy-notice :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount" /> <diy-notice :component="component" :global="data.global" :index="index" />
</template> </template>
<template v-if="component.componentName == 'RubikCube'"> <template v-if="component.componentName == 'RubikCube'">
<diy-rubik-cube :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount" /> <diy-rubik-cube :component="component" :global="data.global" :index="index" />
</template> </template>
<template v-if="component.componentName == 'Text'"> <template v-if="component.componentName == 'Text'">
<diy-text :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount" /> <diy-text :component="component" :global="data.global" :index="index" />
</template> </template>
<template v-if="component.componentName == 'RichText'"> <template v-if="component.componentName == 'RichText'">
<diy-rich-text :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount"/> <diy-rich-text :component="component" :global="data.global" :index="index"/>
</template> </template>
<template v-if="component.componentName == 'ActiveCube'"> <template v-if="component.componentName == 'ActiveCube'">
<diy-active-cube :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount"/> <diy-active-cube :component="component" :global="data.global" :index="index"/>
</template> </template>
<template v-if="component.componentName == 'FloatBtn'"> <template v-if="component.componentName == 'FloatBtn'">
<diy-float-btn :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount"/> <diy-float-btn :component="component" :global="data.global" :index="index"/>
</template> </template>
<template v-if="component.componentName == 'CarouselSearch'"> <template v-if="component.componentName == 'CarouselSearch'">
<diy-carousel-search :scrollBool="diyGroup.componentsScrollBool.CarouselSearch" :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount" /> <diy-carousel-search :scrollBool="diyGroup.componentsScrollBool.CarouselSearch" :component="component" :global="data.global" :index="index" />
</template> </template>
<template v-if="component.componentName == 'PictureShow'"> <template v-if="component.componentName == 'PictureShow'">
<diy-picture-show :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount" /> <diy-picture-show :component="component" :global="data.global" :index="index" />
</template> </template>
<template v-if="component.componentName == 'FormSubmit'">
<diy-form-submit :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormInput'">
<diy-form-input ref="diyFormInputRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormTextarea'">
<diy-form-textarea ref="diyFormTextareaRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormIdentity'">
<diy-form-identity ref="diyFormIdentityRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormEmail'">
<diy-form-email ref="diyFormEmailRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormMobile'">
<diy-form-mobile ref="diyFormMobileRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormWechatName'">
<diy-form-wechat-name ref="diyFormWechatNameRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormNumber'">
<diy-form-number ref="diyFormNumberRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormRadio'">
<diy-form-radio ref="diyFormRadioRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormCheckbox'">
<diy-form-checkbox ref="diyFormCheckboxRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormTable'">
<diy-form-table ref="diyFormTableRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormDate'">
<diy-form-date ref="diyFormDateRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormDateScope'">
<diy-form-date-scope ref="diyFormDateScopeRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormTime'">
<diy-form-time ref="diyFormTimeRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormTimeScope'">
<diy-form-time-scope ref="diyFormTimeScopeRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormLocation'">
<diy-form-location ref="diyFormLocationRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormAddress'">
<diy-form-address ref="diyFormAddressRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormImage'">
<diy-form-image ref="diyFormImageRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormVideo'">
<diy-form-video ref="diyFormVideoRef" :component="component" :global="data.global" :index="index" />
</template>
<template v-if="component.componentName == 'FormFile'">
<diy-form-file ref="diyFormFileRef" :component="component" :global="data.global" :index="index" />
</template>
</view> </view>
</view> </view>
<template v-if="diyStore.mode == '' && data.global.bottomTabBarSwitch"> <template v-if="diyStore.mode == '' && data.global && data.global.bottomTabBarSwitch">
<view class="pt-[20rpx]"></view> <view class="pt-[20rpx]"></view>
<tabbar /> <tabbar />
</template> </template>
@ -65,19 +126,22 @@
import { useDiyGroup } from './useDiyGroup' import { useDiyGroup } from './useDiyGroup'
import useDiyStore from '@/app/stores/diy'; import useDiyStore from '@/app/stores/diy';
import { ref } from 'vue'; import { ref,getCurrentInstance } from 'vue';
const props = defineProps(['data', 'pullDownRefreshCount']); const props = defineProps(['data']);
const instance: any = getCurrentInstance();
const getFormRef = () => {
return {
componentRefs: instance.refs
}
}
const topTabbarRef = ref(null);
const diyStore = useDiyStore(); const diyStore = useDiyStore();
const diyGroup = useDiyGroup({ const diyGroup = useDiyGroup({
...props, ...props,
getFormRef() { getFormRef
return {
topTabbarRef: topTabbarRef.value
}
}
}); });
const data = ref(diyGroup.data) const data = ref(diyGroup.data)
@ -89,7 +153,8 @@
diyGroup.onPageScroll() diyGroup.onPageScroll()
defineExpose({ defineExpose({
refresh: diyGroup.refresh refresh: diyGroup.refresh,
getFormRef
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -106,9 +106,35 @@ export function useDiyGroup(params: any = {}) {
// 页面onShow调用时也会触发改方法 // 页面onShow调用时也会触发改方法
const refresh = () => { const refresh = () => {
nextTick(() => { nextTick(() => {
params.getFormRef().topTabbarRef?.refresh(); let time: any = null;
let fn = () => {
diyStore.componentRefs = params.getFormRef().componentRefs;
data.value.componentRefs = params.getFormRef().componentRefs;
params.getFormRef().componentRefs.topTabbarRef?.refresh();
if (time) clearInterval(time);
}
let getPass = () => {
let pass = false;
try {
params.getFormRef()
pass = true;
} catch (e) {
pass = false;
}
if (pass) {
fn();
}
return pass;
}
if (!getPass()) {
time = setInterval(() => {
getPass()
}, 100)
}
}) })
} }

View File

@ -1,13 +0,0 @@
<template>
<view class="fixed-group">
<template v-if="props.data.global.component == 'demo-index'">
<fixed-demo-index :data="props.data" :pullDownRefreshCount="props.pullDownRefreshCount"></fixed-demo-index>
</template>
</view>
</template>
<script lang="ts" setup>
const props = defineProps(['data','pullDownRefreshCount']);
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

View File

@ -1,8 +0,0 @@
<template>
</template>
<script setup lang="ts">
</script>
<style lang="scss">
</style>

View File

@ -82,6 +82,7 @@ export function wechatLogin(data : AnyObject) {
} }
return request.post('wechat/login', data, { showErrorMessage: false }) return request.post('wechat/login', data, { showErrorMessage: false })
} }
/** /**
* openid * openid
*/ */
@ -105,6 +106,7 @@ export function weappLogin(data : AnyObject) {
export function updateWeappOpenid(data: AnyObject) { export function updateWeappOpenid(data: AnyObject) {
return request.put('weapp/update_openid', data, { showErrorMessage: false }) return request.put('weapp/update_openid', data, { showErrorMessage: false })
} }
/** /**
* *
*/ */

View File

@ -0,0 +1,29 @@
import request from '@/utils/request'
/**
*
*/
export function getDiyFormInfo(params: Record<string, any>) {
return request.get(`diy/form`,params)
}
/**
*
*/
export function addFormRecord(params: Record<string, any>) {
return request.post('diy/form/record', params)
}
/**
*
*/
export function getFormResultInfo(params: Record<string, any>) {
return request.get('diy/form/result', params)
}
/**
*
*/
export function getFormRecord(params: Record<string, any>) {
return request.get(`diy/form/record`,params)
}

View File

@ -56,6 +56,16 @@ export function bindMobile(data: AnyObject) {
return request.put('member/mobile', data, { showErrorMessage: true }) return request.put('member/mobile', data, { showErrorMessage: true })
} }
/**
*
*/
export function getMobile(data: AnyObject) {
if (uni.getStorageSync('pid')) {
data.pid = uni.getStorageSync('pid');
}
return request.put('member/getmobile', data, { showErrorMessage: true })
}
/** /**
* *
*/ */

View File

@ -70,6 +70,12 @@ export function fetchBase64Image(data: AnyObject) {
return request.post('file/image/base64', data) return request.post('file/image/base64', data)
} }
/**
*
*/
export function uploadVideo(data: AnyObject) {
return request.upload('file/video', data, { showErrorMessage: true })
}
/** /**
* *
*/ */

View File

@ -118,7 +118,7 @@
import useDiyStore from '@/app/stores/diy'; import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common'; import { img } from '@/utils/common';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']); const props = defineProps(['component', 'index']);
const diyStore = useDiyStore(); const diyStore = useDiyStore();
@ -209,7 +209,7 @@
if (diyComponent.value.topElementRounded) style += 'border-top-right-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;'; if (diyComponent.value.topElementRounded) style += 'border-top-right-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;';
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;'; if (diyComponent.value.bottomElementRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;';
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;'; if (diyComponent.value.bottomElementRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;';
style += 'overflow: hidden'; style += 'overflow: hidden;';
return style; return style;
} }
@ -219,13 +219,6 @@
return style; return style;
}; };
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
onMounted(() => { onMounted(() => {
refresh(); refresh();
// //

View File

@ -149,7 +149,7 @@
const systemInfo = uni.getSystemInfoSync(); const systemInfo = uni.getSystemInfoSync();
const instance = getCurrentInstance(); const instance = getCurrentInstance();
const props = defineProps(['component', 'index', 'pullDownRefreshCount', 'global', 'scrollBool']); const props = defineProps(['component', 'index', 'global', 'scrollBool']);
const diyStore = useDiyStore(); const diyStore = useDiyStore();
const diyComponent = computed(() => { const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') { if (diyStore.mode == 'decorate') {
@ -183,14 +183,6 @@
return style; return style;
}) })
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
const moduleHeight:any = ref('') const moduleHeight:any = ref('')
const setModuleLocation = ()=> { const setModuleLocation = ()=> {
nextTick(() => { nextTick(() => {

View File

@ -32,7 +32,7 @@
import useDiyStore from '@/app/stores/diy'; import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common'; import { img } from '@/utils/common';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']); const props = defineProps(['component', 'index']);
const diyStore = useDiyStore(); const diyStore = useDiyStore();
@ -113,12 +113,6 @@
} }
}) })
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -0,0 +1,96 @@
<template>
<view :style="warpCss">
表单 地址组件
<view class="relative">
<view class="p-[10rpx] flex items-center ">
<view class="w-[27%] mr-[10rpx] flex items-center">
<text class="text-overflow-ellipsis" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="text-[#ec0003]">{{ diyComponent.field.required ? '*' : '' }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import { img } from '@/utils/common';
import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index','global']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormAddress') {
refresh();
}
}
)
}else{
}
});
const refresh = ()=> {
}
//
const verify = () => {
const res = { code: true, message: '' }
// todo diyComponent.value.field.value
return res;
}
//
const reset = () => {
// todo diyComponent.value.field.value = '';
}
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
.text-overflow-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

View File

@ -0,0 +1,325 @@
<template>
<view v-if="diyComponent.viewFormDetail" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="detail-one-content">
<view class="detail-one-content-label">{{ diyComponent.field.name }}</view>
<view class="flex detail-one-content-value">
<view class="" v-for="(item,index) in diyComponent.field.value" :key="index" >{{ item.text}}<text v-if="index !== diyComponent.field.value.length - 1"></text></view>
</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<view class="detail-two-content">
<view class="detail-two-content-label">{{ diyComponent.field.name }}</view>
<view class="detail-two-content-value flex w-[80%] justify-end">
<view class="" v-for="(item,index) in diyComponent.field.value" :key="index" >{{ item.text}}<text v-if="index !== diyComponent.field.value.length - 1"></text></view>
</view>
</view>
</view>
</view>
<view v-else :style="warpCss" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="layout-one-label">
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="is-hidden">{{ t('hidden') }}</text>
</view>
<view v-if="diyComponent.field.remark.text" class="layout-one-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-one-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view class="layout-one-content" v-if="diyComponent.style == 'style-1'">
<u-checkbox-group v-model="selectValue" @change="checkboxChange" iconPlacement="left">
<view v-for="(item, index) in diyComponent.options" :key="index" class="mr-[40rpx]">
<u-checkbox activeColor="var(--primary-color)" :labelSize="(diyComponent.fontSize * 2) + 'rpx'" :labelColor="diyComponent.textColor" :label="item.text" :name="item.id"></u-checkbox>
</view>
</u-checkbox-group>
</view>
<u-checkbox-group v-if="diyComponent.style == 'style-2'" v-model="selectValue" @change="checkboxChange" iconPlacement="left" placement="column">
<view v-for="(item, index) in diyComponent.options" :key="index" @click="selectRadio(item)" class="layout-one-content mb-[16rpx]" :class="{'!mb-[0]': (diyComponent.options.length-1) == index}">
<u-checkbox class="!m-[0]" activeColor="var(--primary-color)" :labelSize="(diyComponent.fontSize * 2) + 'rpx'" :labelColor="diyComponent.textColor" :label="item.text" :name="item.id"></u-checkbox>
</view>
</u-checkbox-group>
<view v-if="diyComponent.style == 'style-3'" @click="openPicker" class="layout-one-content justify-between">
<view v-if="diyComponent.field.value && diyComponent.field.value.length">
<text class="mr-[10rpx] text-[28rpx]" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" v-for="(item,index) in diyComponent.field.value">{{item.text}}<text v-if="index !== diyComponent.field.value.length - 1">,</text></text>
</view>
<text v-else class="text-[28rpx] text-[#999]" :style="{'font-size': (diyComponent.fontSize * 2) + 'rpx'}">{{checkboxPlaceholder}}</text>
<text class="nc-iconfont nc-icon-xiaV6xx pull-down-arrow text-[#666]" :class="{'selected': selectShow}" :style="{'font-size': (diyComponent.fontSize * 2+2) + 'rpx !important'}"></text>
</view>
<view class="layout-one-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-one-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="layout-two-is-hidden">{{ t('hidden') }}</text>
<view class="layout-two-wrap" :class="{'!pb-[20rpx]': ((diyComponent.style == 'style-2' || diyComponent.style == 'style-3') && diyGlobal.borderControl),'no-border': !diyGlobal.borderControl}">
<view class="layout-two-label" :class="{'justify-start': diyGlobal.completeAlign == 'left', 'justify-end': diyGlobal.completeAlign == 'right'}">
<text class="required" v-if="diyComponent.field.required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
</view>
<view class="layout-two-content" v-if= "diyComponent.style == 'style-1'">
<u-checkbox-group v-model="selectValue" @change="checkboxChange" iconPlacement="left" class="justify-end">
<view v-for="(item, index) in diyComponent.options" class="ml-[30rpx]">
<u-checkbox activeColor="var(--primary-color)" :labelSize="(diyComponent.fontSize * 2) + 'rpx'" :labelColor="diyComponent.textColor" :key="index" :label="item.text" :name="item.id"></u-checkbox>
</view>
</u-checkbox-group>
</view>
<view class="layout-two-content" v-if="diyComponent.style == 'style-2'">
<view class="justify-end w-full">
<u-checkbox-group v-model="selectValue" placement="column" @change="checkboxChange" iconPlacement="left">
<view v-for="(item, index) in diyComponent.options" :key="index" @click="selectRadio(item)" class="border-solid border-[2rpx] border-[#e6e6e6] rounded-[10rpx] flex items-center h-[80rpx] mb-[16rpx] px-[16rpx] box-border" :class="{'mb-[0]': diyComponent.options.length == (index+1)}">
<u-checkbox activeColor="var(--primary-color)" :labelSize="(diyComponent.fontSize * 2) + 'rpx'" :labelColor="diyComponent.textColor" class="!m-[0]" :label="item.text" :name="item.id"></u-checkbox>
</view>
</u-checkbox-group>
</view>
</view>
<view class="layout-two-content" v-if= "diyComponent.style == 'style-3'">
<view @click="openPicker" class="px-[16rpx] box-border h-[80rpx] flex items-center justify-between border-solid border-[2rpx] border-[#e6e6e6] rounded-[10rpx] w-[100%]">
<view v-if="diyComponent.field.value && diyComponent.field.value.length">
<text class="mr-[10rpx] text-[28rpx]" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" v-for="(item,index) in diyComponent.field.value">{{item.text}}<text v-if="index !== diyComponent.field.value.length - 1">,</text></text>
</view>
<text v-else class="text-[28rpx] text-[#999]" :style="{'font-size': (diyComponent.fontSize * 2) + 'rpx'}">{{checkboxPlaceholder}}</text>
<text class="nc-iconfont nc-icon-xiaV6xx pull-down-arrow text-[#666]" :class="{'selected': selectShow}" :style="{'font-size': (diyComponent.fontSize * 2+2) + 'rpx !important'}"></text>
</view>
</view>
</view>
<view class="layout-two-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view v-if="diyComponent.field.remark.text" class="layout-two-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-two-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-two-attribute-item">{{item.title}}</view>
</view>
</view>
<!-- 样式三下拉弹窗 -->
<u-popup :show="selectShow" mode="bottom" @close="selectShow=false" >
<view class="p-[15rpx]">
<scroll-view scroll-y="true" class="max-h-[450rpx] px-[14rpx] box-border">
<u-checkbox-group v-model="pullDownVal" placement="column" iconPlacement="right">
<view v-for="(item, index) in diyComponent.options" :key="index" class="border-solid border-[0] border-b-[2rpx] border-[#e6e6e6] py-[20rpx]">
<u-checkbox activeColor="var(--primary-color)" :labelSize="'30rpx'" labelColor="#333" :style="{'width': '100%'}" :label="item.text" :name="item.id"></u-checkbox>
</view>
</u-checkbox-group>
</scroll-view>
<view class="flex items-center pt-[20rpx]">
<view @click="pullDownCancelFn" class="flex-1 flex justify-center h-[70rpx] leading-[70rpx] text-[#333] bg-[#eee] text-[26rpx] border-[0] font-500 rounded-[10rpx] mr-[20rpx]">{{ t('cancel') }}</view>
<view @click="pullDownConfirmFn" class="flex-1 flex justify-center bg-[var(--primary-color)] h-[70rpx] leading-[70rpx] text-[#fff] text-[26rpx] border-[0] rounded-[10rpx]">{{ t('confirm') }}</view>
</view>
</view>
</u-popup>
<view v-if="diyStore.mode == 'decorate'" class="form-item-mask"></view>
<!-- 隐私弹窗 -->
<form-privacy-pop ref="formPrivacyRef" :data="formPrivacyData" />
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import { img } from '@/utils/common';
import { t } from '@/locale'
import useDiyStore from '@/app/stores/diy';
import formPrivacyPop from './../form-privacy-pop/index.vue';
const props = defineProps(['component', 'index','global']);
const diyStore = useDiyStore();
const selectValue = ref([])
const selectShow = ref(false);
const pullDownVal = ref([])
const errorInfo:any = ref(null);
const formPrivacyRef: any = ref(null);
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const diyGlobal = computed(() => {
return props.global;
})
const formPrivacyData = computed(() => {
let str = `${diyComponent.value.field.name}已开启隐私保护,提交后会部分打码,只有你自己和管理员才能查看完整信息`;
return str;
})
// input
const inputAttribute = ()=>{
let arr = [];
if(diyComponent.value.autofill){
let obj = {
title: '已自动填充'
};
arr.push(obj);
}
if(diyComponent.value.field.privacyProtection){
let obj = {
title: '已开启隐私保护',
type: 'privacy'
};
arr.push(obj);
}
arr.forEach((item,index,arr)=>{
if(index != (arr.length-1)){
let obj = {
title: '|'
};
arr.push(obj);
}
})
return arr;
}
const eventFn = (type:any)=>{
if(type == 'privacy'){
//
formPrivacyRef.value.open();
}
}
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
else if (diyComponent.value.componentEndBgColor) style += 'background-color:' + diyComponent.value.componentEndBgColor + ';';
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormCheckbox') {
refresh();
}
}
)
}else{
}
if(diyComponent.value.style == 'style-3'){
pullDownVal.value = diyComponent.value.field.value.map(item => item.id);
}
if(diyComponent.value.field.value.length>0){
selectValue.value = diyComponent.value.field.value.map(item => item.id);
}
});
const refresh = ()=> {
// console.log('diyComponent.value.field.value',diyComponent.value.field.value)
}
const checkboxPlaceholder = computed(() => {
let str = '';
str += `请选择${ diyComponent.value.field.name }`
return str;
})
//
const verify = () => {
const res = { code: true, message: '' }
if (diyComponent.value.field.required && diyComponent.value.field.value.length == '' && diyStore.mode != 'decorate') {
res.code = false
res.message = checkboxPlaceholder.value;
}
errorInfo.value = res;
return res;
}
const backupValue = ref([]);
//
const reset = () => {
pullDownVal.value = [];
selectValue.value = [];
diyComponent.value.field.value = [];
}
const pullDownCancelFn = ()=>{
selectShow.value = false;
pullDownVal.value = backupValue.value.map(item => item.id);
}
const pullDownConfirmFn = () => {
selectShow.value = false;
const selectedItems = diyComponent.value.options.filter(item => pullDownVal.value.includes(item.id));
diyComponent.value.field.value = selectedItems.map(item => ({ id: item.id, text: item.text }));
}
const isDisabled = computed(() => {
return diyStore.mode === 'decorate';
});
const openPicker = () => {
if(isDisabled.value){
return
}
backupValue.value = [...diyComponent.value.field.value]; //
selectShow.value = true;
}
// store
const checkboxChange = (value: any) => {
const selectedItems = diyComponent.value.options.filter(item => value.includes(item.id));
diyComponent.value.field.value = selectedItems.map(item => ({ id: item.id, text: item.text }));
};
const selectRadio = (item) => {
if (isDisabled.value) {
return;
}
const itemId = item.id;
const index = selectValue.value.indexOf(itemId);
if (index > -1) {
selectValue.value.splice(index, 1);
} else {
selectValue.value.push(itemId);
}
const selectedItems = diyComponent.value.options.filter(item => selectValue.value.includes(item.id));
diyComponent.value.field.value = selectedItems.map(item => ({ id: item.id, text: item.text }));
};
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
.pull-down-arrow{
transition: all .3s;
transform: rotate(0);
&.selected{
transform: rotate(180deg);
}
}
.form-item-frame :deep(.u-checkbox .u-checkbox__icon-wrap){
width: 30rpx !important;
height: 30rpx !important;
}
</style>

View File

@ -0,0 +1,314 @@
<template>
<view v-if="diyComponent.viewFormDetail" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="detail-one-content" v-if="diyComponent.field.value.start.date || diyComponent.field.value.end.date">
<text class="detail-one-content-label">{{ diyComponent.field.name }}</text>
<view class="detail-one-content-value">
<text>{{ diyComponent.field.value.start.date }} </text>
<text v-if="diyComponent.field.value.start.date && diyComponent.field.value.end.date">-</text>
<text>{{ diyComponent.field.value.end.date }}</text>
</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<view class="detail-two-content" v-if="diyComponent.field.value.start.date || diyComponent.field.value.end.date">
<text class="detail-two-content-label">{{ diyComponent.field.name }}</text>
<view class="detail-two-content-value w-[80%]">
<text>{{ diyComponent.field.value.start.date }}</text>
<text v-if="diyComponent.field.value.start.date && diyComponent.field.value.end.date">-</text>
<text> {{ diyComponent.field.value.end.date }}</text>
</view>
</view>
</view>
</view>
<view v-else :style="warpCss" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="layout-one-label">
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="is-hidden">{{ t('hidden') }}</text>
</view>
<view v-if="diyComponent.field.remark.text" class="layout-one-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-one-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view class="flex items-center">
<view class="layout-one-content flex-1" @click="openCalendar">
<view class="nc-iconfont nc-icon-a-riliV6xx-36 !text-[32rpx] text-[#999] mr-[16rpx]"></view>
<view class="flex-1 text-overflow-ellipsis" :class="{'!text-[#999]' : !diyComponent.field.value.start.timestamp && !diyComponent.defaultControl}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' }">
{{ startDate }}
</view>
</view>
<view class="mx-[10rpx]" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}">-</view>
<view class="layout-one-content flex-1" @click="openCalendar">
<view class="nc-iconfont nc-icon-a-riliV6xx-36 !text-[32rpx] text-[#999] mr-[16rpx]"></view>
<view class="flex-1 text-overflow-ellipsis" :class="{'!text-[#999]' : !diyComponent.field.value.end.timestamp && !diyComponent.defaultControl}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}">
{{ endDate }}
</view>
</view>
</view>
<view class="layout-one-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-one-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="layout-two-is-hidden">{{ t('hidden') }}</text>
<view class="layout-two-wrap" :class="{'no-border': !diyGlobal.borderControl}">
<view class="layout-two-label" :class="{'justify-start': diyGlobal.completeAlign == 'left', 'justify-end': diyGlobal.completeAlign == 'right'}">
<text class="required" v-if="diyComponent.field.required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
</view>
<view class="layout-two-content" @click="openCalendar">
<view class="text-overflow-ellipsis flex justify-center" :class="{'!text-[#999]' : !diyComponent.field.value.start.timestamp && !diyComponent.defaultControl}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}">
{{ startDate }}
</view>
<view class="mx-[10rpx]" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}">-</view>
<view class="text-overflow-ellipsis flex justify-center" :class="{'!text-[#999]' : !diyComponent.field.value.end.timestamp && !diyComponent.defaultControl}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}">
{{ endDate }}
</view>
<text class="nc-iconfont !text-[#666] !text-[36rpx] nc-icon-youV6xx -mr-[8rpx]"></text>
</view>
</view>
<view class="layout-two-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view v-if="diyComponent.field.remark.text" class="layout-two-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-two-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-two-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="calendar-wrap">
<u-calendar :show="calendarShow" mode="range" @confirm="confirm" @close="calendarShow=false" closeOnClickOverlay="true"
:defaultDate="defaultDate" startText="开始" endText="结束" confirmDisabledText="禁止选择" color="var(--primary-color)" ref="calendar" monthNum="2"></u-calendar>
</view>
<!-- 遮罩层装修使用 -->
<view v-if="diyStore.mode == 'decorate'" class="form-item-mask"></view>
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import { img,timeTurnTimeStamp } from '@/utils/common';
import { t } from '@/locale'
import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index','global']);
const diyStore = useDiyStore();
const calendarShow = ref(false);
const errorInfo:any = ref(null);
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const diyGlobal = computed(() => {
return props.global;
})
// input
const inputAttribute = ()=>{
let arr = [];
if(diyComponent.value.autofill){
let obj = {
title: '已自动填充'
};
arr.push(obj);
}
arr.forEach((item,index,arr)=>{
if(index != (arr.length-1)){
let obj = {
title: '|'
};
arr.push(obj);
}
})
return arr;
}
const eventFn = (type:any)=>{
}
//
const startDate = computed(() => {
let returnDate = '';
let date = '';
if(diyComponent.value.field.value.start.date){
let timestamp = diyComponent.value.field.value.start.timestamp;
returnDate = getDateFn(timestamp);
date = returnDate;
}else{
if(diyComponent.value.start.defaultControl){
if(diyComponent.value.start.dateWay == 'current'){
returnDate = getDateFn();
}else if(diyComponent.value.start.dateWay == 'diy'){
let timestamp = diyComponent.value.field.default.start.timestamp ? diyComponent.value.field.default.start.timestamp : '';
returnDate = getDateFn(timestamp);
}
date = returnDate;
}else{
returnDate = diyComponent.value.start.placeholder;
date = '';
}
}
diyComponent.value.field.value.start.date = date;
diyComponent.value.field.value.start.timestamp = date ? timeTurnTimeStamp(date) : 0;
return returnDate;
})
//
const endDate = computed(() => {
let returnDate = '';
let date = '';
if (diyComponent.value.field.value.end.date) {
let timestamp = diyComponent.value.field.value.end.timestamp;
returnDate = getDateFn(timestamp);
date = returnDate;
diyComponent.value.field.value.end.date = date;
diyComponent.value.field.value.end.timestamp = timeTurnTimeStamp(date);
} else {
if (diyComponent.value.end.defaultControl) {
if (diyComponent.value.end.dateWay == 'current') {
returnDate = getDateFn();
} else if (diyComponent.value.end.dateWay == 'diy') {
returnDate = getDateFn(diyComponent.value.field.default.end.timestamp);
}
date = returnDate;
diyComponent.value.field.value.end.date = date;
diyComponent.value.field.value.end.timestamp = timeTurnTimeStamp(date);
} else {
returnDate = diyComponent.value.end.placeholder;
}
}
return returnDate;
})
//
const defaultDate = computed(()=>{
let arr = [];
arr[0] = getDateFn(timeTurnTimeStamp(startDate.value), 'YYYY-MM-DD');
arr[1] = getDateFn(timeTurnTimeStamp(endDate.value), 'YYYY-MM-DD');
return arr;
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
else if (diyComponent.value.componentEndBgColor) style += 'background-color:' + diyComponent.value.componentEndBgColor + ';';
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormDateScope') {
refresh();
}
}
)
}
});
const refresh = ()=> {
// todo
// {{ diyComponent.start.defaultControl ? diyComponent.field.value.start.date : diyComponent.start.placeholder }}
}
//
const verify = () => {
const res = { code: true, message: '' }
if(diyComponent.value.field.required){
if (diyComponent.value.field.value.start.date == '' || !diyComponent.value.field.value.start.timestamp) {
res.code = false
res.message = `请选择${ diyComponent.value.start.placeholder }`;
}else if (diyComponent.value.field.value.end.date == '' || !diyComponent.value.field.value.end.timestamp) {
res.code = false
res.message = `请选择${ diyComponent.value.end.placeholder }`;
}
}
errorInfo.value = res;
return res;
}
//
const reset = () => {
diyComponent.value.field.value.start.date = '';
diyComponent.value.field.value.start.timestamp = 0;
diyComponent.value.field.value.end.date = '';
diyComponent.value.field.value.end.timestamp = 0;
}
const openCalendar = () =>{
if(diyStore.mode === 'decorate') return;
calendarShow.value = true;
}
const confirm = (e)=> {
diyComponent.value.field.value.start.date = e[0];
diyComponent.value.field.value.start.timestamp = timeTurnTimeStamp(e[0]); // todo
diyComponent.value.field.value.end.date = e[e.length - 1];
diyComponent.value.field.value.end.timestamp = timeTurnTimeStamp(e[e.length - 1]); // todo
calendarShow.value = false;
}
const getDateFn = (res:any='', type:any=diyComponent.value.dateFormat)=>{
let date = res ? new Date(res*1000) : new Date();
let year = date.getFullYear();
let month = String(date.getMonth() + 1).padStart(2, '0');
let day = String(date.getDate()).padStart(2, '0');
let str = '';
if(type == 'YYYY年M月D日'){
str = `${year}${month}${day}`;
}else if(type == 'YYYY-MM-DD'){
str = `${year}-${month}-${day}`;
}else if(type == 'YYYY/MM/DD'){
str = `${year}/${month}/${day}`;
}
return str;
}
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
.text-overflow-ellipsis {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
display: inline-block;
}
</style>
<style lang="scss">
.calendar-wrap .u-calendar .u-calendar-header__title, .u-calendar-header__subtitle, .u-calendar-month__title{
display: block;
}
</style>

View File

@ -0,0 +1,272 @@
<template>
<view v-if="diyComponent.viewFormDetail" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="detail-one-content">
<text class="detail-one-content-label">{{ diyComponent.field.name }}</text>
<text class="detail-one-content-value">{{ diyComponent.field.value.date}}</text>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<view class="detail-two-content">
<text class="detail-two-content-label">{{ diyComponent.field.name }}</text>
<view class="detail-two-content-value w-[80%]">{{ diyComponent.field.value.date}}</view>
</view>
</view>
</view>
<view v-else :style="warpCss" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="layout-one-label">
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="is-hidden">{{ t('hidden') }}</text>
</view>
<view v-if="diyComponent.field.remark.text" class="layout-one-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-one-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view class="layout-one-content" @click="openCalendar">
<view class="nc-iconfont nc-icon-a-riliV6xx-36 !text-[32rpx] text-[#999] mr-[16rpx]"></view>
<view class="flex-1 text-overflow-ellipsis flex" :class="{'!text-[#999]' : !diyComponent.field.value.date && !diyComponent.defaultControl}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' }">
{{ startDate }}
</view>
</view>
<view class="layout-one-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-one-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="layout-two-is-hidden">{{ t('hidden') }}</text>
<view class="layout-two-wrap" :class="{'no-border': !diyGlobal.borderControl}">
<view class="layout-two-label" :class="{'justify-start': diyGlobal.completeAlign == 'left', 'justify-end': diyGlobal.completeAlign == 'right'}">
<text class="required" v-if="diyComponent.field.required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
</view>
<view class="layout-two-content" @click="openCalendar">
<view class="flex-1 text-overflow-ellipsis flex justify-end" :class="{'!text-[#999]' : !diyComponent.field.value.date && !diyComponent.defaultControl}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}">
{{ startDate }}
</view>
<text class="nc-iconfont !text-[#666] !text-[36rpx] nc-icon-youV6xx -mr-[8rpx]"></text>
</view>
</view>
<view class="layout-two-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view v-if="diyComponent.field.remark.text" class="layout-two-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-two-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-two-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="calendar-wrap">
<u-calendar :show="calendarShow" mode="single" @confirm="confirm" @close="calendarShow=false" closeOnClickOverlay="true"
:formatter="formatter" confirmDisabledText="禁止选择" color="var(--primary-color)" ref="calendar" :maxDate="maxDate"></u-calendar>
<u-datetime-picker :show="show" v-model="diyComponent.field.value.date" mode="datetime" @cancel="show=false" closeOnClickOverlay="true" @confirm="calendarConfirm" @close="show=false" :minDate="minDate"></u-datetime-picker>
</view>
<!-- 遮罩层装修使用 -->
<view v-if="diyStore.mode == 'decorate'" class="form-item-mask"></view>
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch, onMounted } from 'vue';
import { img, timeStampTurnTime, timeTurnTimeStamp } from '@/utils/common';
import { t } from '@/locale'
import useDiyStore from '@/app/stores/diy';
let currentDate = new Date();
currentDate.setFullYear(currentDate.getFullYear() + 1);
const maxDate = ref(currentDate.getTime());
let currentDateTime = new Date();
const minDate = ref(currentDateTime.getTime());
const props = defineProps(['component', 'index','global']);
const diyStore = useDiyStore();
const calendarShow = ref(false);
const show = ref(false);
const errorInfo:any = ref(null);
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const diyGlobal = computed(() => {
return props.global;
})
// input
const inputAttribute = ()=>{
let arr = [];
if(diyComponent.value.autofill){
let obj = {
title: '已自动填充'
};
arr.push(obj);
}
arr.forEach((item,index,arr)=>{
if(index != (arr.length-1)){
let obj = {
title: '|'
};
arr.push(obj);
}
})
return arr;
}
const eventFn = (type:any)=>{
}
//
const startDate = computed(() => {
var date = '';
if(diyComponent.value.field.value.date){
let timestamp = diyComponent.value.field.value.timestamp;
date = getDateFn(timestamp);
diyComponent.value.field.value.date = date;
diyComponent.value.field.value.timestamp = timeTurnTimeStamp(date);
}else{
if(diyComponent.value.defaultControl){
if(diyComponent.value.dateWay == 'current'){
date = getDateFn();
}else if(diyComponent.value.dateWay == 'diy'){
let timestamp = diyComponent.value.field.default.timestamp || '';
date = getDateFn(timestamp);
}
diyComponent.value.field.value.date = date;
diyComponent.value.field.value.timestamp = timeTurnTimeStamp(date);
}else{
date = diyComponent.value.placeholder;
}
}
return date;
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
else if (diyComponent.value.componentEndBgColor) style += 'background-color:' + diyComponent.value.componentEndBgColor + ';';
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormDate') {
refresh();
}
}
)
}
});
const refresh = ()=> {
// todo
// {{ diyComponent.defaultControl ? diyComponent.value.field.value.date : diyComponent.placeholder }}
}
//
const verify = () => {
const res = { code: true, message: '' }
if (diyComponent.value.field.required && (!diyComponent.value.field.value || !diyComponent.value.field.value.timestamp)) {
res.code = false
res.message = `请选择${ diyComponent.value.placeholder }`;
}
errorInfo.value = res;
return res;
}
//
const reset = () => {
diyComponent.value.field.value.date = '';
diyComponent.value.field.value.timestamp = '';
}
const openCalendar = () =>{
if(diyStore.mode === 'decorate') return;
if(diyComponent.value.dateFormat=='YYYY-MM-DD HH:mm'){
show.value = true
}else{
calendarShow.value = true;
}
}
const confirm = (e)=> {
diyComponent.value.field.value.date = e[0];
diyComponent.value.field.value.timestamp = timeTurnTimeStamp(e[0]); // todo
calendarShow.value = false;
}
const calendarConfirm = (e)=>{
diyComponent.value.field.value.date = e.value;
diyComponent.value.field.value.timestamp = timeTurnTimeStamp(e.value); // todo
show.value = false;
}
const formatter = (day) => {
const time = timeStampTurnTime(Date.parse(day.date)/1000,"year_month_day");
return day
}
const getDateFn = (data:any='', type:any=diyComponent.value.dateFormat)=>{
let date = data ? new Date(data*1000) : new Date();
let year = date.getFullYear();
let month = String(date.getMonth() + 1).padStart(2, '0');
let day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
let str = '';
if(type == 'YYYY年M月D日'){
str = `${year}${month}${day}`;
}else if(type == 'YYYY-MM-DD'){
str = `${year}-${month}-${day}`;
}else if(type == 'YYYY/MM/DD'){
str = `${year}/${month}/${day}`;
}else if(type == 'YYYY-MM-DD HH:mm'){
str = `${year}-${month}-${day} ${hours}:${minutes}`;
}
return str;
}
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
</style>
<style lang="scss">
.form-item-frame .calendar-wrap .u-calendar{
:deep(.u-calendar-header__title), :deep(.u-calendar-header__subtitle), :deep(.u-calendar-month__title){
display: block;
}
}
.form-item-frame :deep(.u-picker .u-toolbar__wrapper__cancel), .form-item-frame :deep(.u-picker .u-toolbar__wrapper__confirm){
font-size: 28rpx;
}
</style>

View File

@ -0,0 +1,200 @@
<template>
<view v-if="diyComponent.viewFormDetail" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="detail-one-content">
<text class="detail-one-content-label">{{ diyComponent.field.name }}</text>
<text class="detail-one-content-value">{{ diyComponent.field.value }}</text>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<view class="detail-two-content">
<view class="">{{ diyComponent.field.name }}</view>
<view class="detail-two-content-value w-[80%]">{{ diyComponent.field.value }}</view>
</view>
</view>
</view>
<view v-else :style="warpCss" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="layout-one-label">
<text class="text-overflow-ellipsis" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="is-hidden">{{ t('hidden') }}</text>
</view>
<view v-if="diyComponent.field.remark.text" class="layout-one-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-one-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<input type="email" class="layout-one-content" :placeholder="inputPlaceholder" placeholderClass="layout-one-input-placeholder" :placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx' }" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" v-model="diyComponent.field.value" :disabled="isDisabled"/>
<view class="layout-one-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-one-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="layout-two-is-hidden">{{ t('hidden') }}</text>
<view class="layout-two-wrap" :class="{'no-border': !diyGlobal.borderControl}">
<view class="layout-two-label" :class="{'justify-start': diyGlobal.completeAlign == 'left', 'justify-end': diyGlobal.completeAlign == 'right'}">
<text class="required" v-if="diyComponent.field.required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
</view>
<input type="email" class="layout-two-content no-flex" :placeholder="inputPlaceholder" placeholderClass="layout-two-input-placeholder" :placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx'}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' }" v-model="diyComponent.field.value" :disabled="isDisabled" />
</view>
<view class="layout-two-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view v-if="diyComponent.field.remark.text" class="layout-two-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-two-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-two-attribute-item">{{item.title}}</view>
</view>
</view>
<view v-if="diyStore.mode == 'decorate'" class="form-item-mask"></view>
<form-privacy-pop ref="formPrivacyRef" :data="formPrivacyData" />
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import { img } from '@/utils/common';
import { t } from '@/locale'
import formPrivacyPop from './../form-privacy-pop/index.vue';
import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index', 'global']);
const diyStore = useDiyStore();
const errorInfo:any = ref(null);
const formPrivacyRef: any = ref(null);
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const diyGlobal = computed(() => {
return props.global;
})
const inputPlaceholder = computed(() => {
let str = '';
str += diyComponent.value.placeholder
return str;
})
const formPrivacyData = computed(() => {
let str = `${diyComponent.value.field.name}已开启隐私保护,提交后会部分打码,只有你自己和管理员才能查看完整信息`;
return str;
})
// input
const inputAttribute = ()=>{
let arr = [];
if(diyComponent.value.autofill){
let obj = {
title: '已自动填充'
};
arr.push(obj);
}
if(diyComponent.value.field.privacyProtection){
let obj = {
title: '已开启隐私保护',
type: 'privacy'
};
arr.push(obj);
}
arr.forEach((item,index,arr)=>{
if(index != (arr.length-1)){
let obj = {
title: '|'
};
arr.push(obj);
}
})
return arr;
}
const eventFn = (type:any)=>{
if(type == 'privacy'){
//
formPrivacyRef.value.open();
}
}
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
else if (diyComponent.value.componentEndBgColor) style += 'background-color:' + diyComponent.value.componentEndBgColor + ';';
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormEmail') {
refresh();
}
}
)
}else{
}
});
const refresh = ()=> {
//
if (diyStore.mode == 'decorate') {
diyComponent.value.field.value = diyComponent.value.field.default;
}else {
//
if (diyComponent.value.field.value == '' && diyComponent.value.field.default) {
diyComponent.value.field.value = diyComponent.value.field.default;
}
}
}
//
const verify = () => {
let emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const res = { code: true, message: '' }
if (diyComponent.value.field.required && diyComponent.value.field.value == '' && diyStore.mode != 'decorate') {
res.code = false
res.message = `${ inputPlaceholder.value }`;
}else if (!emailRegex.test(diyComponent.value.field.value) && diyStore.mode != 'decorate') {
res.code = false
res.message = `邮箱格式不正确,请重新输入`;
}
errorInfo.value = res;
return res;
}
//
const reset = () => {
diyComponent.value.field.value = '';
}
const isDisabled = computed(() => {
return diyStore.mode == 'decorate';
});
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
</style>

View File

@ -0,0 +1,119 @@
<template>
<view :style="warpCss">
<view class="relative">
<view class="p-[10rpx] flex items-center ">
<view class="w-[27%] mr-[10rpx] flex items-center">
<text class="text-overflow-ellipsis" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="text-[#ec0003]">{{ diyComponent.field.required ? '*' : '' }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import { img } from '@/utils/common';
import useDiyStore from '@/app/stores/diy';
import { uploadImage } from '@/app/api/system'
const props = defineProps(['component', 'index','global']);
const diyStore = useDiyStore();
const selectValue = ref([])
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormFile') {
refresh();
}
}
)
}
});
const imgListPreview = computed(() => {
return selectValue.value.map(item => {
return {url: img(item)}
})
})
const afterRead = (event) => {
event.file.forEach(item => {
uploadImage({
filePath: item.url,
name: 'file'
}).then(res => {
if (selectValue.value.length < diyComponent.value.limit ) {
selectValue.value.push(res.data.url)
}
}).catch(() => {
})
})
}
const deletePic = (event)=> {
selectValue.value.splice(event.index, 1)
}
const refresh = ()=> {
}
//
const verify = () => {
const res = { code: true, message: '' }
// todo diyComponent.value.field.value
return res;
}
//
const reset = () => {
// todo diyComponent.value.field.value = '';
}
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
.text-overflow-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

View File

@ -0,0 +1,44 @@
<template>
<view @touchmove.prevent.stop>
<u-popup :show="formDetailPrivacyPop" @close="closeFn" zIndex="500" mode="center" :round="8">
<view class="w-[570rpx] popup-common center">
<view class="text-center my-5">
{{ t('tips') }}<br/>{{data}}
</view>
<view class="flex justify-between">
<button class="w-[50%] h-[100rpx] rounded-[0rpx] leading-[100rpx] !bg-[transform] border-solid border-[0] border-t-[2rpx] border-[#e6e6e6] !text-[#333]" @click="copy(data)">{{ t('copy') }}</button>
<button class="w-[50%] h-[100rpx] rounded-[0rpx] border-solid border-[0] border-t-[2rpx] border-l-[2rpx] bo border-[#e6e6e6] leading-[100rpx] !bg-[transform] !text-[var(--primary-color)]" @click="closeFn">{{ t('know') }}</button>
</view>
</view>
</u-popup>
</view>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { t } from '@/locale'
import { copy } from '@/utils/common';
const props = defineProps(['data']);
const data = computed(() => {
let str = props.data || '已开启隐私保护,提交后会部分打码,只有你自己和管理员才能查看完整信息';
return str;
})
const formDetailPrivacyPop = ref(false);
const open = ()=>{
formDetailPrivacyPop.value = true
}
const closeFn = ()=>{
formDetailPrivacyPop.value = false
}
defineExpose({
open
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
</style>

View File

@ -0,0 +1,228 @@
<template>
<view v-if="diyComponent.viewFormDetail" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="detail-one-content">
<text class="detail-one-content-label">{{ diyComponent.field.name }}</text>
<view class="detail-one-content-value">
<text>{{ formattedIdentity }}</text>
<text v-if="diyComponent.field.privacyProtection" class="ml-[20rpx] text-[var(--primary-color)]" @click="viewPrivacy">{{ t('view') }}</text>
</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<view class="detail-two-content">
<text class="detail-two-content-label">{{ diyComponent.field.name }}</text>
<view class="detail-one-content-value">
<text>{{ formattedIdentity }}</text>
<text v-if="diyComponent.field.privacyProtection" class="ml-[20rpx] text-[var(--primary-color)]" @click="viewPrivacy">{{ t('view') }}</text>
</view>
</view>
</view>
<form-identity-privacy ref="formDetailPrivacyRef" :data="diyComponent.field.value" />
</view>
<view v-else :style="warpCss" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="layout-one-label">
<text class="text-overflow-ellipsis" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="is-hidden">{{ t('hidden') }}</text>
</view>
<view v-if="diyComponent.field.remark.text" class="layout-one-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-one-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<input type="idcard" class="layout-one-content" :placeholder="inputPlaceholder" placeholderClass="layout-one-input-placeholder" :placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx'}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" v-model="diyComponent.field.value" :disabled="isDisabled" maxlength="18"/>
<view class="layout-one-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-one-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="layout-two-is-hidden">{{ t('hidden') }}</text>
<view class="layout-two-wrap" :class="{'no-border': !diyGlobal.borderControl}">
<view class="layout-two-label" :class="{'justify-start': diyGlobal.completeAlign == 'left', 'justify-end': diyGlobal.completeAlign == 'right'}">
<text class="required" v-if="diyComponent.field.required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
</view>
<input type="idcard" class="layout-two-content no-flex" :placeholder="inputPlaceholder" placeholderClass="layout-two-input-placeholder" :placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx'}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" v-model="diyComponent.field.value" :disabled="isDisabled" maxlength="18" />
</view>
<view class="layout-two-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view v-if="diyComponent.field.remark.text" class="layout-two-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-two-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-two-attribute-item">{{item.title}}</view>
</view>
</view>
<view v-if="diyStore.mode == 'decorate'" class="form-item-mask"></view>
<form-privacy-pop ref="formPrivacyRef" :data="formPrivacyData" />
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted,nextTick } from 'vue';
import { img } from '@/utils/common';
import { t } from '@/locale'
import formPrivacyPop from './../form-privacy-pop/index.vue';
import useDiyStore from '@/app/stores/diy';
import formIdentityPrivacy from './../form-identity-privacy/index.vue'
const props = defineProps(['component', 'index', 'global']);
const diyStore = useDiyStore();
const errorInfo:any = ref(null);
const formPrivacyRef: any = ref(null);
const formDetailPrivacyRef: any = ref(null);
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const diyGlobal = computed(() => {
return props.global;
})
const formattedIdentity = computed(() => {
const identity = String(diyComponent.value.field.value);
if (diyComponent.value.field.privacyProtection) {
return identity.replace(/(\d{3})\d*(\d{4})/, '$1****$2');
}
return identity;
});
const inputPlaceholder = computed(() => {
let str = '';
str += diyComponent.value.placeholder
return str;
})
const formPrivacyData = computed(() => {
let str = `${diyComponent.value.field.name}已开启隐私保护,提交后会部分打码,只有你自己和管理员才能查看完整信息`;
return str;
})
// input
const inputAttribute = ()=>{
let arr = [];
if(diyComponent.value.autofill){
let obj = {
title: '已自动填充'
};
arr.push(obj);
}
if(diyComponent.value.field.privacyProtection){
let obj = {
title: '已开启隐私保护',
type: 'privacy'
};
arr.push(obj);
}
arr.forEach((item,index,arr)=>{
if(index != (arr.length-1)){
let obj = {
title: '|'
};
arr.push(obj);
}
})
return arr;
}
const eventFn = (type:any)=>{
if(type == 'privacy'){
//
formPrivacyRef.value.open();
}
}
const viewPrivacy = () => {
nextTick(() => {
if (formDetailPrivacyRef.value) {
formDetailPrivacyRef.value.open();
} else {
console.warn('formDetailPrivacyRef is not defined');
}
});
}
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
else if (diyComponent.value.componentEndBgColor) style += 'background-color:' + diyComponent.value.componentEndBgColor + ';';
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormIdentity') {
refresh();
}
}
)
}else{
}
});
const refresh = ()=> {
//
if (diyStore.mode == 'decorate') {
diyComponent.value.field.value = diyComponent.value.field.default;
}else {
//
if (diyComponent.value.field.value == '' && diyComponent.value.field.default) {
diyComponent.value.field.value = diyComponent.value.field.default;
}
}
}
//
const verify = () => {
const res = { code: true, message: '' }
// 18
let idCardReg = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))\d{3}[0-9Xx]$/;
if (diyComponent.value.field.required && diyComponent.value.field.value == '' && diyStore.mode != 'decorate') {
res.code = false
res.message = `${ inputPlaceholder.value }`;
}else if (!idCardReg.test(diyComponent.value.field.value) && diyStore.mode != 'decorate') {
res.code = false
res.message = `身份证格式不正确,请重新输入`;
}
errorInfo.value = res;
return res;
}
//
const reset = () => {
diyComponent.value.field.value = '';
}
const isDisabled = computed(() => {
return diyStore.mode == 'decorate';
});
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
</style>

View File

@ -0,0 +1,315 @@
<template>
<view v-if="diyComponent.viewFormDetail" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="detail-one-content">
<text class="detail-one-content-label">{{ diyComponent.field.name }}</text>
<view class="flex flex-wrap">
<view class="relative w-[180rpx] !h-[180rpx] mr-[16rpx] mb-[16rpx] " v-for="(item,index) in diyComponent.field.value" :key="index">
<image class="w-[100%] h-[100%]" :src="img(item)" @click="handleImg(item,index)" mode="aspectFill"></image>
</view>
</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<view class="detail-two-content">
<text class="detail-two-content-label">{{ diyComponent.field.name }}</text>
<view class="flex flex-wrap w-[80%] justify-end">
<view class="relative w-[180rpx] !h-[180rpx] mr-[16rpx] mb-[16rpx] " v-for="(item,index) in diyComponent.field.value" :key="index">
<image class="w-[100%] h-[100%]" :src="img(item)" @click="handleImg(item,index)" mode="aspectFill"></image>
</view>
</view>
</view>
</view>
</view>
<view v-else :style="warpCss" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="layout-one-label">
<text class="text-overflow-ellipsis" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="is-hidden">{{ t('hidden') }}</text>
</view>
<view v-if="diyComponent.field.remark.text" class="layout-one-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-one-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view class="flex flex-wrap">
<view class="relative w-[180rpx] !h-[180rpx] mr-[16rpx] mb-[16rpx] layout-one-content" v-for="(item,index) in diyComponent.field.value" :key="index">
<image class="w-[100%] h-[100%]" :src="img(item)" mode="aspectFill"></image>
<view class="absolute top-0 right-[0] bg-[#373737] flex justify-end h-[28rpx] w-[28rpx] rounded-bl-[40rpx]" @click="deleteImage(index)">
<text class="nc-iconfont nc-icon-guanbiV6xx !text-[20rpx] mt-[2rpx] mr-[2rpx] text-[#fff]"></text>
</view>
</view>
<view class="flex items-center flex-1" v-if="diyComponent.uploadMode.length > 1 && diyComponent.field.value.length <= 0">
<view class="layout-one-content !p-[0] flex-1 !items-stretch !h-[100rpx]">
<u-upload accept="image" @afterRead="afterRead" multiple :maxCount="9" capture="camera">
<view class="flex items-center h-[100%] w-[100%] pl-[30rpx] box-border">
<text class="nc-iconfont nc-icon-xiangjiV6xx"></text>
<text class="text-[28rpx] ml-[10rpx]">拍照上传</text>
</view>
</u-upload>
</view>
<view class="layout-one-content !p-[0] ml-[20rpx] !items-stretch flex-1 !h-[100rpx]">
<u-upload accept="image" @afterRead="afterRead" multiple :maxCount="9" capture="album">
<view class="flex items-center h-[100%] w-[100%] pl-[30rpx] box-border">
<text class="nc-iconfont nc-icon-tupiandaohangpc"></text>
<text class="text-[28rpx] ml-[10rpx]">从相册中选择</text>
</view>
</u-upload>
</view>
</view>
<view v-else-if="diyComponent.field.value.length < Number(diyComponent.limit)" class="layout-one-content h-[180rpx] w-[180rpx] !px-[0]">
<u-upload accept="image" v-if="diyComponent.uploadMode.length == 1" @afterRead="afterRead" multiple :capture="uploadType" :maxCount="Number(diyComponent.limit)">
<view class="flex flex-col items-center justify-center w-[180rpx] h-[180rpx] ">
<text class="nc-iconfont !text-[36rpx] mb-[16rpx]" :class="{'nc-icon-xiangjiV6xx':diyComponent.uploadMode.indexOf('take_pictures') > -1, 'nc-icon-tupiandaohangpc':diyComponent.uploadMode.indexOf('take_pictures') == -1}"></text>
<text class="text-[28rpx] ml-[10rpx] text-[24rpx]">{{diyComponent.uploadMode.indexOf('take_pictures') > -1 ? '拍照上传' : '从相册选择'}}</text>
</view>
</u-upload>
<u-upload accept="image" v-else @afterRead="afterRead" multiple :capture="uploadType" :maxCount="Number(diyComponent.limit)">
<view class="flex flex-col items-center justify-center w-[180rpx] h-[180rpx] ">
<text class="nc-iconfont !text-[40rpx] mb-[16rpx] nc-icon-jiahaoV6xx"></text>
<text class="text-[28rpx] ml-[10rpx] text-[24rpx]"> {{ t('uploadTips') }}</text>
</view>
</u-upload>
</view>
</view>
<view class="layout-one-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-one-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="layout-two-is-hidden">{{ t('hidden') }}</text>
<view class="layout-two-wrap" :class="{'no-border': !diyGlobal.borderControl}">
<view class="layout-two-label" :class="{'justify-start': diyGlobal.completeAlign == 'left', 'justify-end': diyGlobal.completeAlign == 'right'}">
<text class="required" v-if="diyComponent.field.required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
</view>
<view class="layout-two-content flex-wrap">
<view class="relative border-box w-[180rpx] !h-[180rpx] ml-[16rpx] mb-[16rpx] border-box border-[2rpx] border-solid border-[#e6e6e6] rounded-[10rpx] flex items-center" v-for="(item,index) in diyComponent.field.value" :key="index">
<image class="w-[100%] h-[100%]" :src="img(item)" mode="aspectFill"></image>
<view class="absolute top-0 right-[0] bg-[#373737] flex justify-end h-[28rpx] w-[28rpx] rounded-bl-[40rpx]" @click="deleteImage(index)">
<text class="nc-iconfont nc-icon-guanbiV6xx !text-[20rpx] mt-[2rpx] mr-[2rpx] text-[#fff]"></text>
</view>
</view>
<view class="items-start border-box border-[2rpx] ml-[16rpx] mb-[16rpx] border-solid border-[#e6e6e6] rounded-[10rpx]">
<u-upload accept="image" v-if="diyComponent.field.value.length < Number(diyComponent.limit)" @afterRead="afterRead" multiple :capture="uploadType" :maxCount="Number(diyComponent.limit)">
<view class="flex flex-col items-center justify-center min-w-[180rpx] min-h-[180rpx]">
<text class="nc-iconfont !text-[40rpx] mb-[16rpx] nc-icon-jiahaoV6xx"></text>
<text class="text-[28rpx] ml-[10rpx] text-[24rpx]"> {{ t('uploadTips') }}</text>
</view>
</u-upload>
</view>
</view>
</view>
<view class="layout-two-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view v-if="diyComponent.field.remark.text" class="layout-two-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-two-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-two-attribute-item">{{item.title}}</view>
</view>
</view>
<view v-if="diyStore.mode == 'decorate'" class="form-item-mask"></view>
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import { img } from '@/utils/common';
import { t } from '@/locale'
import { onLoad,onShow,onUnload} from '@dcloudio/uni-app';
import useDiyStore from '@/app/stores/diy';
import { uploadImage } from '@/app/api/system'
const props = defineProps(['component', 'index','global']);
const diyStore = useDiyStore();
const selectValue = ref([])
const errorInfo:any = ref(null);
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const diyGlobal = computed(() => {
return props.global;
})
//
const uploadType = computed(()=>{
let type = [];
if(diyComponent.value && diyComponent.value.uploadMode){
diyComponent.value.uploadMode.forEach((item,index)=>{
if(item == 'take_pictures' && diyComponent.value.uploadMode.indexOf('camera') == -1){
type.push('camera');
}else if(item == 'select_from_album' && diyComponent.value.uploadMode.indexOf('album') == -1){
type.push('album');
}
})
}
return type;
});
// input
const inputAttribute = ()=>{
let arr = [];
if(diyComponent.value.autofill){
let obj = {
title: '已自动填充'
};
arr.push(obj);
}
arr.forEach((item,index,arr)=>{
if(index != (arr.length-1)){
let obj = {
title: '|'
};
arr.push(obj);
}
})
return arr;
}
const eventFn = (type:any)=>{
}
const handleImg = (url: any,index: number) => {
let tmp = [];
if(diyComponent.value.field.value){
tmp = Object.values(diyComponent.value.field.value).map((item: any) => {
return img(item);
});
}
uni.previewImage({
current: index,
urls: tmp,
indicator: "number",
loop: true
})
}
//
onUnload(()=>{
// #ifdef H5 || APP
try {
uni.closePreviewImage()
}catch (e) {
}
// #endif
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormImage') {
refresh();
}
}
)
}
});
const isDisabled = computed(() => {
return diyStore.mode === 'decorate';
});
const imgListPreview = computed(() => {
return diyComponent.value.field.value.map(item => {
return {url: img(item)}
})
})
const afterRead = (event) => {
uni.setStorageSync('sku_form_refresh', true);
event.file.forEach(item => {
upload(item);
})
}
const upload = (data:any) => {
if (diyComponent.value.field.value.length > Number(diyComponent.value.limit) ) {
uni.showToast({ title: `最多允许上传${diyComponent.value.limit}张图片`, icon: 'none' })
return false
}
uploadImage({
filePath: data.url,
name: 'file'
}).then(res => {
if (diyComponent.value.field.value.length < Number(diyComponent.value.limit) ) {
diyComponent.value.field.value.push(res.data.url)
}
}).catch(() => {
})
}
const deleteImage = (event)=> {
diyComponent.value.field.value.splice(event.index, 1)
}
const refresh = ()=> {
}
//
const verify = () => {
const res = { code: true, message: '' }
// todo diyComponent.value.field.value
if (diyComponent.value.field.required && (!diyComponent.value.field.value || diyComponent.value.field.value && !diyComponent.value.field.value.length)) {
res.code = false
res.message = `请上传图片`;
}else if (diyComponent.value.field.value && diyComponent.value.field.value.length > Number(diyComponent.value.limit)) {
res.code = false
res.message = `图片上传数量已超出限制数量`;
}
errorInfo.value = res;
return res;
}
//
const reset = () => {
// todo diyComponent.value.field.value = '';
diyComponent.value.field.value = [];
}
defineExpose({
verify,
reset
})
</script>
<style lang="scss">
.layout-one-content .u-upload .u-upload__wrap > uni-view, .layout-one-content .u-upload .u-upload__wrap > view, .layout-one-content .u-upload .u-upload__wrap > div{
width: 100%;
}
</style>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
</style>

View File

@ -0,0 +1,196 @@
<template>
<view v-if="diyComponent.viewFormDetail" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="detail-one-content">
<text class="detail-one-content-label">{{ diyComponent.field.name }}</text>
<text class="detail-one-content-value">{{ diyComponent.field.value }}</text>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<view class="detail-two-content">
<view class="">{{ diyComponent.field.name }}</view>
<view class="detail-two-content-value w-[80%]">{{ diyComponent.field.value }}</view>
</view>
</view>
</view>
<view v-else :style="warpCss" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="layout-one-label">
<text class="text-overflow-ellipsis" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="is-hidden">{{ t('hidden') }}</text>
</view>
<view v-if="diyComponent.field.remark.text" class="layout-one-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-one-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<input type="text" class="layout-one-content" :placeholder="inputPlaceholder" placeholderClass="layout-one-input-placeholder" :placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx' }" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" v-model="diyComponent.field.value" :disabled="isDisabled" />
<view class="layout-one-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-one-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="layout-two-is-hidden">{{ t('hidden') }}</text>
<view class="layout-two-wrap" :class="{'no-border': !diyGlobal.borderControl}">
<view class="layout-two-label" :class="{'justify-start': diyGlobal.completeAlign == 'left', 'justify-end': diyGlobal.completeAlign == 'right'}">
<text class="required" v-if="diyComponent.field.required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
</view>
<input type="text" class="layout-two-content no-flex" :placeholder="inputPlaceholder" placeholderClass="layout-two-input-placeholder" :placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx'}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" v-model="diyComponent.field.value" :disabled="isDisabled" />
</view>
<view class="layout-two-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view v-if="diyComponent.field.remark.text" class="layout-two-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-two-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-two-attribute-item">{{item.title}}</view>
</view>
</view>
<view v-if="diyStore.mode == 'decorate'" class="form-item-mask"></view>
<form-privacy-pop ref="formPrivacyRef" :data="formPrivacyData" />
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import { img } from '@/utils/common';
import { t } from '@/locale'
import formPrivacyPop from './../form-privacy-pop/index.vue';
import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index', 'global']);
const diyStore = useDiyStore();
const errorInfo:any = ref(null);
const formPrivacyRef: any = ref(null);
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const diyGlobal = computed(() => {
return props.global;
})
const inputPlaceholder = computed(() => {
let str = '';
str += diyComponent.value.placeholder
return str;
})
const formPrivacyData = computed(() => {
let str = `${diyComponent.value.field.name}已开启隐私保护,提交后会部分打码,只有你自己和管理员才能查看完整信息`;
return str;
})
// input
const inputAttribute = ()=>{
let arr = [];
if(diyComponent.value.autofill){
let obj = {
title: '已自动填充'
};
arr.push(obj);
}
if(diyComponent.value.field.privacyProtection){
let obj = {
title: '已开启隐私保护',
type: 'privacy'
};
arr.push(obj);
}
arr.forEach((item,index,arr)=>{
if(index != (arr.length-1)){
let obj = {
title: '|'
};
arr.push(obj);
}
})
return arr;
}
const eventFn = (type:any)=>{
if(type == 'privacy'){
//
formPrivacyRef.value.open();
}
}
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
else if (diyComponent.value.componentEndBgColor) style += 'background-color:' + diyComponent.value.componentEndBgColor + ';';
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormInput') {
refresh();
}
}
)
}else{
}
});
const refresh = ()=> {
//
if (diyStore.mode == 'decorate') {
diyComponent.value.field.value = diyComponent.value.field.default;
}else {
//
if (diyComponent.value.field.value == '' && diyComponent.value.field.default) {
diyComponent.value.field.value = diyComponent.value.field.default;
}
}
}
//
const verify = () => {
const res = { code: true, message: '' }
if (diyComponent.value.field.required && diyComponent.value.field.value == '' && diyStore.mode != 'decorate') {
res.code = false
res.message = `${ inputPlaceholder.value }`;
}
errorInfo.value = res;
return res;
}
//
const reset = () => {
diyComponent.value.field.value = '';
}
const isDisabled = computed(() => {
return diyStore.mode == 'decorate';
});
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
</style>

View File

@ -0,0 +1,96 @@
<template>
<view :style="warpCss">
form-location
<view class="relative">
<view class="p-[10rpx] flex items-center ">
<view class="w-[27%] mr-[10rpx] flex items-center">
<text class="text-overflow-ellipsis" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="text-[#ec0003]">{{ diyComponent.field.required ? '*' : '' }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import { img } from '@/utils/common';
import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index','global']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormLocation') {
refresh();
}
}
)
}else{
}
});
const refresh = ()=> {
}
//
const verify = () => {
const res = { code: true, message: '' }
// todo diyComponent.value.field.value
return res;
}
//
const reset = () => {
// todo diyComponent.value.field.value = '';
}
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
.text-overflow-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

View File

@ -0,0 +1,227 @@
<template>
<view v-if="diyComponent.viewFormDetail" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="detail-one-content">
<text class="detail-one-content-label">{{ diyComponent.field.name }}</text>
<view class="detail-one-content-value">
<text>{{ formattedPhoneNumber }}</text>
<text v-if="diyComponent.field.privacyProtection" class="ml-[20rpx] text-[var(--primary-color)]" @click="viewPrivacy">{{ t('view') }}</text>
</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<view class="detail-two-content">
<text class="detail-two-content-label">{{ diyComponent.field.name }}</text>
<view class="detail-two-content-value">
<text>{{ formattedPhoneNumber }}</text>
<text v-if="diyComponent.field.privacyProtection" class="ml-[20rpx] text-[var(--primary-color)]" @click="viewPrivacy">{{ t('view') }}</text>
</view>
</view>
</view>
<form-privacy ref="formDeatilPrivacyRef" :data="diyComponent.field.value" />
</view>
<view v-else :style="warpCss" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="layout-one-label">
<text class="text-overflow-ellipsis" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="is-hidden">{{ t('hidden') }}</text>
</view>
<view v-if="diyComponent.field.remark.text" class="layout-one-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-one-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<input type="number" class="layout-one-content" :placeholder="inputPlaceholder" placeholderClass="layout-one-input-placeholder" :placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx' }" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" v-model="diyComponent.field.value" :disabled="isDisabled" maxlength="11"/>
<view class="layout-one-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-one-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="layout-two-is-hidden">{{ t('hidden') }}</text>
<view class="layout-two-wrap" :class="{'no-border': !diyGlobal.borderControl}">
<view class="layout-two-label" :class="{'justify-start': diyGlobal.completeAlign == 'left', 'justify-end': diyGlobal.completeAlign == 'right'}">
<text class="required" v-if="diyComponent.field.required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
</view>
<input type="number" class="layout-two-content no-flex" :placeholder="inputPlaceholder" placeholderClass="layout-two-input-placeholder" :placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx'}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" v-model="diyComponent.field.value" :disabled="isDisabled" maxlength="11" />
</view>
<view class="layout-two-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view v-if="diyComponent.field.remark.text" class="layout-two-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-two-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-two-attribute-item">{{item.title}}</view>
</view>
</view>
<view v-if="diyStore.mode == 'decorate'" class="form-item-mask"></view>
<form-privacy-pop ref="formPrivacyRef" :data="formPrivacyData" />
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted,nextTick } from 'vue';
import { img } from '@/utils/common';
import { t } from '@/locale'
import formPrivacyPop from './../form-privacy-pop/index.vue';
import useDiyStore from '@/app/stores/diy';
import formPrivacy from './../form-privacy/index.vue'
const props = defineProps(['component', 'index', 'global']);
const diyStore = useDiyStore();
const errorInfo:any = ref(null);
const formPrivacyRef: any = ref(null);
const formDeatilPrivacyRef: any = ref(null);
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const diyGlobal = computed(() => {
return props.global;
})
const formattedPhoneNumber = computed(() => {
const phone = String(diyComponent.value.field.value);
if (diyComponent.value.field.privacyProtection) {
return phone.replace(/(\d{3})\d{4}(\d{3})/, '$1****$2');
}
return phone;
});
const inputPlaceholder = computed(() => {
let str = '';
str += diyComponent.value.placeholder
return str;
})
const formPrivacyData = computed(() => {
let str = `${diyComponent.value.field.name}已开启隐私保护,提交后会部分打码,只有你自己和管理员才能查看完整信息`;
return str;
})
// input
const inputAttribute = ()=>{
let arr = [];
if(diyComponent.value.autofill){
let obj = {
title: '已自动填充'
};
arr.push(obj);
}
if(diyComponent.value.field.privacyProtection){
let obj = {
title: '已开启隐私保护',
type: 'privacy'
};
arr.push(obj);
}
arr.forEach((item,index,arr)=>{
if(index != (arr.length-1)){
let obj = {
title: '|'
};
arr.push(obj);
}
})
return arr;
}
const eventFn = (type:any)=>{
if(type == 'privacy'){
//
formPrivacyRef.value.open();
}
}
const viewPrivacy = () => {
nextTick(() => {
if (formDeatilPrivacyRef.value) {
formDeatilPrivacyRef.value.open();
} else {
console.warn('formDeatilPrivacyRef is not defined');
}
});
}
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
else if (diyComponent.value.componentEndBgColor) style += 'background-color:' + diyComponent.value.componentEndBgColor + ';';
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormMobile') {
refresh();
}
}
)
}else{
}
});
const refresh = ()=> {
//
if (diyStore.mode == 'decorate') {
diyComponent.value.field.value = diyComponent.value.field.default;
}else {
//
if (diyComponent.value.field.value == '' && diyComponent.value.field.default) {
diyComponent.value.field.value = diyComponent.value.field.default;
}
}
}
//
const verify = () => {
const res = { code: true, message: '' }
if (diyComponent.value.field.required && diyComponent.value.field.value == '' && diyStore.mode != 'decorate') {
res.code = false
res.message = `${ inputPlaceholder.value }`;
}else if (!/^1[3-9]\d{9}$/.test(diyComponent.value.field.value) && diyStore.mode != 'decorate') {
res.code = false
res.message = `手机号格式不正确,请重新输入`;
}
errorInfo.value = res;
return res;
}
//
const reset = () => {
diyComponent.value.field.value = '';
}
const isDisabled = computed(() => {
return diyStore.mode == 'decorate';
});
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
</style>

View File

@ -0,0 +1,202 @@
<template>
<view v-if="diyComponent.viewFormDetail" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="detail-one-content">
<text class="detail-one-content-label">{{ diyComponent.field.name }}</text>
<text class="detail-one-content-value">{{ diyComponent.field.value }}</text>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<view class="detail-two-content">
<text class="detail-two-content-label">{{ diyComponent.field.name }}</text>
<view class="detail-two-content-value w-[80%]">{{ diyComponent.field.value }}</view>
</view>
</view>
</view>
<view v-else :style="warpCss" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="layout-one-label">
<text class="text-overflow-ellipsis" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="is-hidden">{{ t('hidden') }}</text>
</view>
<view v-if="diyComponent.field.remark.text" class="layout-one-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-one-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view class="layout-one-content flex items-center">
<input type="number" class="flex-1" :placeholder="inputPlaceholder" placeholderClass="layout-one-input-placeholder" :placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx'}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" v-model="diyComponent.field.value" :disabled="isDisabled" />
<text class="text-[#999] text-[28rpx]" v-if="diyComponent.unit">{{diyComponent.unit}}</text>
</view>
<view class="layout-one-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-one-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="layout-two-is-hidden">{{ t('hidden') }}</text>
<view class="layout-two-wrap" :class="{'no-border': !diyGlobal.borderControl}">
<view class="layout-two-label" :class="{'justify-start': diyGlobal.completeAlign == 'left', 'justify-end': diyGlobal.completeAlign == 'right'}">
<text class="required" v-if="diyComponent.field.required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
</view>
<view class="layout-two-content">
<input type="number" :placeholder="inputPlaceholder" placeholderClass="layout-two-input-placeholder" :placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx'}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" v-model="diyComponent.field.value" :disabled="isDisabled" />
<text class="text-[#999] ml-[10rpx] pt-[2rpx] text-[28rpx]" v-if="diyComponent.unit">{{diyComponent.unit}}</text>
</view>
</view>
<view class="layout-two-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view v-if="diyComponent.field.remark.text" class="layout-two-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-two-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-two-attribute-item">{{item.title}}</view>
</view>
</view>
<view v-if="diyStore.mode == 'decorate'" class="form-item-mask"></view>
<form-privacy-pop ref="formPrivacyRef" :data="formPrivacyData" />
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import { img } from '@/utils/common';
import { t } from '@/locale'
import formPrivacyPop from './../form-privacy-pop/index.vue';
import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index', 'global']);
const diyStore = useDiyStore();
const errorInfo:any = ref(null);
const formPrivacyRef: any = ref(null);
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const diyGlobal = computed(() => {
return props.global;
})
const inputPlaceholder = computed(() => {
let str = '';
str += diyComponent.value.placeholder
return str;
})
const formPrivacyData = computed(() => {
let str = `${diyComponent.value.field.name}已开启隐私保护,提交后会部分打码,只有你自己和管理员才能查看完整信息`;
return str;
})
// input
const inputAttribute = ()=>{
let arr = [];
if(diyComponent.value.autofill){
let obj = {
title: '已自动填充'
};
arr.push(obj);
}
if(diyComponent.value.field.privacyProtection){
let obj = {
title: '已开启隐私保护',
type: 'privacy'
};
arr.push(obj);
}
arr.forEach((item,index,arr)=>{
if(index != (arr.length-1)){
let obj = {
title: '|'
};
arr.push(obj);
}
})
return arr;
}
const eventFn = (type:any)=>{
if(type == 'privacy'){
//
formPrivacyRef.value.open();
}
}
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
else if (diyComponent.value.componentEndBgColor) style += 'background-color:' + diyComponent.value.componentEndBgColor + ';';
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormNumber') {
refresh();
}
}
)
}else{
}
});
const refresh = ()=> {
//
if (diyStore.mode == 'decorate') {
diyComponent.value.field.value = diyComponent.value.field.default;
}else {
//
if (diyComponent.value.field.value == '' && diyComponent.value.field.default) {
diyComponent.value.field.value = diyComponent.value.field.default;
}
}
}
//
const verify = () => {
const res = { code: true, message: '' }
if (diyComponent.value.field.required && diyComponent.value.field.value == '' && diyStore.mode != 'decorate') {
res.code = false
res.message = `${ inputPlaceholder.value }`;
}
errorInfo.value = res;
return res;
}
//
const reset = () => {
diyComponent.value.field.value = '';
}
const isDisabled = computed(() => {
return diyStore.mode == 'decorate';
});
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
</style>

View File

@ -0,0 +1,43 @@
<template>
<view @touchmove.prevent.stop>
<u-popup :show="formPrivacyPop" @close="closeFn" zIndex="500" mode="center" :round="8">
<view class="flex flex-col items-center w-[640rpx] pt-[50rpx]">
<view class="text-[32rpx] font-bold">{{ t('prompt') }}</view>
<view class="text-center px-[40rpx] py-[30rpx] leading-[1.5] min-h-[90rpx]">
{{data}}
</view>
<view class="flex items-center justify-center border-solid border-[0] border-t-[2rpx] border-[#e6e6e6] w-[100%] h-[90rpx] text-[28rpx]">
<text @click="closeFn">{{ t('know') }}</text>
</view>
</view>
</u-popup>
</view>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { t } from '@/locale'
const props = defineProps(['data']);
const data = computed(() => {
let str = props.data || '已开启隐私保护,提交后会部分打码,只有你自己和管理员才能查看完整信息';
return str;
})
const formPrivacyPop = ref(false);
const open = ()=>{
formPrivacyPop.value = true
}
const closeFn = ()=>{
formPrivacyPop.value = false
}
defineExpose({
open
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
</style>

View File

@ -0,0 +1,49 @@
<template>
<view @touchmove.prevent.stop>
<u-popup :show="formDetailPrivacyPop" @close="closeFn" zIndex="500" mode="center" :round="8">
<view class="w-[570rpx] popup-common center">
<view class="text-center my-5">
{{ t('tips') }}<br/>{{data}}
</view>
<view class="flex justify-between">
<button class="w-[50%] h-[100rpx] rounded-[0rpx] leading-[100rpx] !bg-[transform] border-solid border-[0] border-t-[2rpx] border-[#e6e6e6] !text-[#333]" @click="call"> {{ t('call') }}</button>
<button class="w-[50%] h-[100rpx] rounded-[0rpx] border-solid border-[0] border-t-[2rpx] border-l-[2rpx] bo border-[#e6e6e6] leading-[100rpx] !bg-[transform] !text-[var(--primary-color)]" @click="closeFn">{{ t('know') }}</button>
</view>
</view>
</u-popup>
</view>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { t } from '@/locale'
const props = defineProps(['data']);
const data = computed(() => {
let str = props.data || '已开启隐私保护,提交后会部分打码,只有你自己和管理员才能查看完整信息';
return str;
})
const formDetailPrivacyPop = ref(false);
const open = ()=>{
formDetailPrivacyPop.value = true
}
const closeFn = ()=>{
formDetailPrivacyPop.value = false
}
const call = ()=>{
uni.makePhoneCall({
phoneNumber: props.data
});
}
defineExpose({
open
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
</style>

View File

@ -0,0 +1,331 @@
<template>
<view v-if="diyComponent.viewFormDetail" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="detail-one-content">
<text class="detail-one-content-label">{{ diyComponent.field.name }}</text>
<text v-for="(item,index) in diyComponent.field.value" :key="index" >{{ item.text}}</text>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<view class="detail-two-content">
<text class="detail-two-content-label">{{ diyComponent.field.name }}</text>
<view class="detail-two-content-value w-[80%]" v-for="(item,index) in diyComponent.field.value" :key="index" >{{ item.text}}</view>
</view>
</view>
</view>
<view v-else :style="warpCss" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="layout-one-label">
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="is-hidden">{{ t('hidden') }}</text>
</view>
<view v-if="diyComponent.field.remark.text" class="layout-one-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-one-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view v-if="diyComponent.style == 'style-1'" class="layout-one-content !flex-initial">
<u-radio-group v-model="selectedRadioId" @change="groupChange" iconPlacement="left">
<view v-for="(item, index) in diyComponent.options" :key="index" class="mr-[40rpx]">
<u-radio activeColor="var(--primary-color)" :labelSize="(diyComponent.fontSize * 2) + 'rpx'" :labelColor="diyComponent.textColor" :label="item.text" :name="item.id"></u-radio>
</view>
</u-radio-group>
</view>
<u-radio-group v-if="diyComponent.style == 'style-2'" v-model="selectedRadioId" @change="groupChange" iconPlacement="left" placement="column">
<view v-for="(item, index) in diyComponent.options" :key="index" @click="selectRadio(item)" class="layout-one-content mb-[16rpx]" :class="{'!mb-[0]': (diyComponent.options.length-1) == index}">
<u-radio activeColor="var(--primary-color)" :labelSize="(diyComponent.fontSize * 2) + 'rpx'" :labelColor="diyComponent.textColor" class="mr-[20rpx]" :label="item.text" :name="item.id"></u-radio>
</view>
</u-radio-group>
<view v-if="diyComponent.style == 'style-3'" @click="openPicker" class="layout-one-content justify-between">
<view v-if="diyComponent.field.value.length>0">
<text class="mr-[10rpx] text-[28rpx]" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}">{{getSelectRadioName}}</text>
</view>
<text v-else class="text-[28rpx] text-[#999]" :style="{'font-size': (diyComponent.fontSize * 2) + 'rpx'}">{{radioPlaceholder}}</text>
<text class="nc-iconfont nc-icon-xiaV6xx pull-down-arrow text-[#666]" :class="{'selected': selectShow}" :style="{'font-size': (diyComponent.fontSize * 2+2) + 'rpx !important'}"></text>
</view>
<view class="layout-one-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-one-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="layout-two-is-hidden">{{ t('hidden') }}</text>
<view class="layout-two-wrap" :class="{'!pb-[20rpx]': ((diyComponent.style == 'style-2' || diyComponent.style == 'style-3') && diyGlobal.borderControl),'no-border': !diyGlobal.borderControl}">
<view class="layout-two-label" :class="{'justify-start': diyGlobal.completeAlign == 'left', 'justify-end': diyGlobal.completeAlign == 'right'}">
<text class="required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
</view>
<view class="layout-two-content" v-if= "diyComponent.style == 'style-1'">
<view class="justify-end">
<u-radio-group v-model="selectedRadioId" @change="groupChange" iconPlacement="left">
<view v-for="(item, index) in diyComponent.options" :key="index" class="ml-[30rpx]">
<u-radio activeColor="var(--primary-color)" :labelSize="(diyComponent.fontSize * 2) + 'rpx'" :labelColor="diyComponent.textColor" :label="item.text" :name="item.id"></u-radio>
</view>
</u-radio-group>
</view>
</view>
<view class="layout-two-content" v-if="diyComponent.style == 'style-2'">
<view class="justify-end w-full">
<u-radio-group v-model="selectedRadioId" @change="groupChange" placement="column" iconPlacement="left">
<view v-for="(item, index) in diyComponent.options" :key="index" @click="selectRadio(item)" class="border-solid border-[2rpx] border-[#e6e6e6] rounded-[10rpx] flex items-center h-[80rpx] mb-[16rpx] px-[16rpx] box-border" :class="{'mb-[0]': diyComponent.options.length == (index+1)}" >
<u-radio activeColor="var(--primary-color)" :labelSize="(diyComponent.fontSize * 2) + 'rpx'" :labelColor="diyComponent.textColor" class="!m-[0]" :label="item.text" :name="item.id"></u-radio>
</view>
</u-radio-group>
</view>
</view>
<view class="layout-two-content" v-if= "diyComponent.style == 'style-3'">
<view @click="openPicker" class="px-[16rpx] box-border h-[80rpx] flex items-center justify-between border-solid border-[2rpx] border-[#e6e6e6] rounded-[10rpx] w-[100%]">
<view v-if="diyComponent.field.value.length>0">
<text class="mr-[10rpx] text-[28rpx]" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}">{{getSelectRadioName}}</text>
</view>
<text v-else class="text-[28rpx] text-[#999]" :style="{'font-size': (diyComponent.fontSize * 2) + 'rpx'}">{{radioPlaceholder}}</text>
<text class="nc-iconfont nc-icon-xiaV6xx pull-down-arrow text-[#666]" :class="{'selected': selectShow}" :style="{'font-size': (diyComponent.fontSize * 2+2) + 'rpx !important'}"></text>
</view>
</view>
</view>
<view class="layout-two-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view v-if="diyComponent.field.remark.text" class="layout-two-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-two-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-two-attribute-item">{{item.title}}</view>
</view>
</view>
<!-- 样式三下拉弹窗 -->
<u-popup :show="selectShow" mode="bottom" @close="selectShow=false" >
<view class="p-[15rpx]">
<scroll-view scroll-y="true" class="max-h-[450rpx] px-[14rpx] box-border">
<u-radio-group v-model="pullDownVal" placement="column" @change="groupChange" iconPlacement="right">
<view class="border-solid border-[0] border-b-[2rpx] border-[#e6e6e6] py-[20rpx]" @click.stop="pullDownConfirmFn(item)" v-for="(item, index) in diyComponent.options" :key="index">
<u-radio activeColor="var(--primary-color)" :labelSize="(diyComponent.fontSize * 2) + 'rpx'" :labelColor="diyComponent.textColor" :style="{'width': '100%'}" :label="item.text" :name="item.id"></u-radio>
</view>
</u-radio-group>
</scroll-view>
</view>
</u-popup>
<view v-if="diyStore.mode == 'decorate'" class="form-item-mask"></view>
<!-- 隐私弹窗 -->
<form-privacy-pop ref="formPrivacyRef" :data="formPrivacyData" />
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import { img } from '@/utils/common';
import { t } from '@/locale'
import useDiyStore from '@/app/stores/diy';
import formPrivacyPop from './../form-privacy-pop/index.vue';
const props = defineProps(['component', 'index','global']);
const diyStore = useDiyStore();
const selectValue = ref([])
const selectShow = ref(false);
const pullDownVal = ref([])
const selectedRadioId = ref(''); // id
const errorInfo:any = ref(null);
const formPrivacyRef: any = ref(null);
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const diyGlobal = computed(() => {
return props.global;
})
const formPrivacyData = computed(() => {
let str = `${diyComponent.value.field.name}已开启隐私保护,提交后会部分打码,只有你自己和管理员才能查看完整信息`;
return str;
})
// input
const inputAttribute = ()=>{
let arr = [];
if(diyComponent.value.autofill){
let obj = {
title: '已自动填充'
};
arr.push(obj);
}
if(diyComponent.value.field.privacyProtection){
let obj = {
title: '已开启隐私保护',
type: 'privacy'
};
arr.push(obj);
}
arr.forEach((item,index,arr)=>{
if(index != (arr.length-1)){
let obj = {
title: '|'
};
arr.push(obj);
}
})
return arr;
}
const eventFn = (type:any)=>{
if(type == 'privacy'){
//
formPrivacyRef.value.open();
}
}
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
else if (diyComponent.value.componentEndBgColor) style += 'background-color:' + diyComponent.value.componentEndBgColor + ';';
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormRadio') {
refresh();
}
}
)
}
//
if(diyComponent.value.style == 'style-3' && diyComponent.value.field.value.length>0){
pullDownVal.value = diyComponent.value.field.value[0].id;
}
if(diyComponent.value.field.value.length>0){
selectedRadioId.value = diyComponent.value.field.value[0].id;
}
});
const refresh = ()=> {
// console.log('diyComponent.value.field.value',diyComponent.value.field.value)
}
const radioPlaceholder = computed(() => {
let str = '';
str += `请选择${ diyComponent.value.field.name }`
return str;
})
//
const verify = () => {
const res = { code: true, message: '' }
if (diyComponent.value.field.required && diyComponent.value.field.value.length == '' && diyStore.mode != 'decorate') {
res.code = false
res.message = radioPlaceholder.value;
}
errorInfo.value = res;
return res;
}
//
const reset = () => {
selectedRadioId.value='';
pullDownVal.value = [];
selectValue.value = [];
diyComponent.value.field.value = [];
}
const pullDownCancelFn = ()=>{
selectShow.value = false;
// pullDownVal.value = diyComponent.value.field.value;
}
const pullDownConfirmFn = (item:any) => {
selectShow.value = false;
pullDownVal.value = item.id;;
diyComponent.value.field.value = [{ id: item.id, text: item.text }];
// diyComponent.value.field.value = pullDownVal.value;
}
//
const getSelectRadioName = computed(() => {
let names = [];
diyComponent.value.field.value.forEach((selectedItem) => {
const item = diyComponent.value.options.find(option => option.id === selectedItem.id);
if (item) {
names.push(item.text);
}
});
return names.join(', ');
});
const isDisabled = computed(() => {
return diyStore.mode === 'decorate';
});
const openPicker = () => {
if(isDisabled.value){
return
}
selectShow.value = true;
}
const groupChange = (value) => {
selectedRadioId.value = value;
const selectedItem = diyComponent.value.options.find(item => item.id === value);
diyComponent.value.field.value = [{ id: selectedItem.id, text: selectedItem.text }];
selectShow.value = false;
};
const selectRadio = (item) => {
if (isDisabled.value) {
return;
}
selectedRadioId.value = item.id;;
diyComponent.value.field.value = [{ id: item.id, text: item.text }];
};
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
.pull-down-arrow{
transition: all .3s;
transform: rotate(0);
&.selected{
transform: rotate(180deg);
}
}
/* 覆盖 u-radio 样式 */
// ::v-deep(.u-radio) {
// border-bottom: 2rpx solid #e6e6e6;
// height: 90rpx;
// }
.form-item-frame :deep(.u-radio-group .u-radio__icon-wrap){
width: 30rpx !important;
height: 30rpx !important;
}
</style>

View File

@ -0,0 +1,348 @@
<template>
<view>
<view class="overflow-hidden">
<view :style="maskLayer"></view>
<view class="relative submit-wrap z-10" :style="warpCss">
<view class="flex flex-col items-center" :style="boxCss">
<view class="w-[100%] h-[86rpx] text-[28rpx] flex items-center justify-center" @click="submit" :style="submitItem">{{ diyComponent.submitBtn.text }}</view>
<view v-if="diyComponent.resetBtn.control" class="w-[100%] h-[86rpx] mt-[20rpx] text-[28rpx] flex items-center justify-center" @click="reset" :style="resetItem">{{ diyComponent.resetBtn.text }}</view>
</view>
</view>
</view>
<view v-if="diyComponent.btnPosition == 'hover_screen_bottom' && diyStore.mode != 'decorate'" class="w-[100%]" :style="submitFillHeight"></view>
<!-- 苹果安全距离辅助计算 -->
<view class="iphone-secure"></view>
<!-- 遮罩层装修使用 -->
<view v-if="diyStore.mode == 'decorate'" class="form-item-mask"></view>
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted, nextTick, getCurrentInstance } from 'vue';
import useDiyStore from '@/app/stores/diy';
import { img, redirect,getValidTime, deepClone } from '@/utils/common';
import { addFormRecord } from '@/app/api/diy_form';
const props = defineProps(['component', 'index', 'global']);
const diyStore = useDiyStore();
const tabbarInfo = ref();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const diyGlobal = computed(() => {
return props.global;
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
else if (diyComponent.value.componentEndBgColor) style += 'background-color:' + diyComponent.value.componentEndBgColor + ';';
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if(diyStore.mode != 'decorate' && diyComponent.value.btnPosition == 'hover_screen_bottom'){
style += "position: fixed !important;";
var height = tabbarInfo.value ? tabbarInfo.value.height : 0;
style += `left: 0;`;
style += `right: 0;`;
if(height && diyGlobal.value.bottomTabBarSwitch){
style += `bottom: ${height}px;`;
}else{
style += `bottom: 0;`;
}
if (diyComponent.value.pageStartBgColor) {
if (diyComponent.value.pageStartBgColor && diyComponent.value.pageEndBgColor) style += `background:linear-gradient(${ diyComponent.value.pageGradientAngle },${ diyComponent.value.pageStartBgColor },${ diyComponent.value.pageEndBgColor });`;
else if (diyComponent.value.pageStartBgColor) style += `background: ${ diyComponent.value.pageStartBgColor };`;
else if (diyComponent.value.pageEndBgColor) style += `background: ${ diyComponent.value.pageEndBgColor };`;
}
if (diyComponent.value.margin) {
if (diyComponent.value.margin.top > 0) {
style += 'padding-top:' + diyComponent.value.margin.top * 2 + 'rpx' + ';';
}
if(height && diyGlobal.value.bottomTabBarSwitch){
style += 'padding-bottom:' + diyComponent.value.margin.bottom * 2 + 'rpx' + ';';
}else{
style += `padding-bottom: ${(diyComponent.value.margin.bottom + iphoneSecureVal.value) * 2}rpx;`;
}
style += 'padding-right:' + diyComponent.value.margin.both * 2 + 'rpx' + ';';
style += 'padding-left:' + diyComponent.value.margin.both * 2 + 'rpx' + ';';
}
}else {
if( diyComponent.value.btnPosition == 'hover_screen_bottom'){
style += "position: fixed !important;";
style += `left: 0;`;
style += `right: 0;`;
style += `bottom: 0;`;
}
}
return style;
})
const boxCss = computed(() => {
var style = '';
if(diyComponent.value.btnPosition == 'hover_screen_bottom'){
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
else if (diyComponent.value.componentEndBgColor) style += 'background-color:' + diyComponent.value.componentEndBgColor + ';';
}
return style;
})
//
const maskLayer = computed(()=>{
var style = '';
if(diyComponent.value.componentBgUrl) {
style += 'position:absolute;top:0;right:0;left:0;bottom:0;';
style += `background: rgba(0,0,0,${diyComponent.value.componentBgAlpha / 10});`;
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
}
return style;
});
//
const resetItem = computed(() => {
var style = '';
style += `color: ${diyComponent.value.resetBtn.color};`;
style += `background-color: ${diyComponent.value.resetBtn.bgColor};`;
if (diyComponent.value.topElementRounded) style += 'border-top-left-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;';
if (diyComponent.value.topElementRounded) style += 'border-top-right-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;';
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;';
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;';
return style;
})
//
const submitItem = computed(() => {
var style = '';
style += `color: ${diyComponent.value.submitBtn.color};`;
style += `background-color: ${diyComponent.value.submitBtn.bgColor};`;
if (diyComponent.value.topElementRounded) style += 'border-top-left-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;';
if (diyComponent.value.topElementRounded) style += 'border-top-right-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;';
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;';
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;';
return style;
})
//
const submitFillHeight = computed(() => {
let style = '';
style += `padding-bottom: ${submitHeight.value}px;`;
return style;
})
const formComponent: any = ref([])
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormSubmit') {
refresh();
}
}
)
} else {
getFormComponent();
watch(
() => diyStore.value,
(newValue, oldValue) => {
if (newValue) {
let formData = getFormSComponentsData(newValue);
if (formData.components.length) {
uni.setStorageSync('diyFormStorage_' + diyStore.id, formData)
}
}
},
{ deep: true }
)
}
nextTick(() => {
tabbarInfo.value = uni.getStorageSync('tabbarInfo')
})
});
const refresh = ()=> {
}
const getFormComponent = () => {
//
for (let i = 0; i < diyStore.value.length; i++) {
let item = diyStore.value[i];
//
if (item.componentType == 'diy_form' && item.componentName != 'FormSubmit') {
formComponent.value.push(item);
}
}
}
const repeat = ref(false)
const submit = () => {
if (diyStore.mode === 'decorate') return
let allPass = true; //
//
for (let i = 0; i < formComponent.value.length; i++) {
let item = formComponent.value[i];
if (item.field.required || item.field.value) {
let refKey = `diy${ item.componentName }Ref`;
let isBreak = false;
if (diyStore.componentRefs[refKey]) {
for (let k = 0; k < diyStore.componentRefs[refKey].length; k++) {
let compRef = diyStore.componentRefs[refKey][k];
let verify = compRef.verify(); //
if (verify && !verify.code) {
isBreak = true;
uni.showToast({
title: verify.message,
icon: 'none'
});
break;
}
}
if (isBreak) {
allPass = false;
break;
}
}
}
}
if (!allPass) return;
if (repeat.value) return
repeat.value = true
let diyFormStorage = uni.getStorageSync('diyFormStorage_' + diyStore.id)
let value = {};
if(diyFormStorage){
value = diyFormStorage.components
}else{
value = getFormSComponentsData(formComponent.value).components;
}
const data = {
form_id: diyStore.id,
value,
relate_id: '' // todo id
}
addFormRecord(data).then((res: any) => {
uni.removeStorageSync('diyFormStorage_' + diyStore.id)
//
redirect({
url: '/app/pages/index/diy_form_result',
param: { record_id: res.data, form_id: diyStore.id },
mode: 'redirectTo'
})
repeat.value = false
}).catch(() => {
repeat.value = false
})
};
//
const reset = () => {
//
for (let i = 0; i < formComponent.value.length; i++) {
let item = formComponent.value[i];
let refKey = `diy${ item.componentName }Ref`;
if (diyStore.componentRefs[refKey]) {
diyStore.componentRefs[refKey].forEach((compRef: any) => {
if (compRef.reset) compRef.reset(item);
})
}
}
return;
}
//
const getFormSComponentsData = (data:any)=>{
let formData: any = {
validTime: getValidTime(5), // 5
components: []
};
data.forEach((item: any) => {
// --
if (item.componentType == 'diy_form' && item.componentName != 'FormSubmit') {
//
let field = deepClone(item.field);
//
delete field.remark; //
delete field.detailComponent; //
delete field.default; //
formData.components.push({
id: item.id,
componentName: item.componentName,
componentType: item.componentType,
componentTitle: item.componentTitle,
isHidden: item.isHidden,
field: field
})
}
});
return formData;
}
let submitHeight = ref(0);
const instance = getCurrentInstance();
let iphoneSecureVal = ref(0);
nextTick(() => {
const query = uni.createSelectorQuery().in(instance);
query.select('.iphone-secure').boundingClientRect((data: any) => {
iphoneSecureVal.value = data ? data.height : 0;
}).exec();
setTimeout(()=>{
query.select('.submit-wrap').boundingClientRect((data: any) => {
submitHeight.value = data ? data.height : 0;
}).exec();
},500)
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
.iphone-secure{
position: absolute;
bottom: 0;
z-index: -1;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
left: 0;
right: 0;
}
</style>

View File

@ -0,0 +1,96 @@
<template>
<view :style="warpCss">
//
<view class="relative">
<view class="p-[10rpx] flex items-center ">
<view class="w-[27%] mr-[10rpx] flex items-center">
<text class="text-overflow-ellipsis" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="text-[#ec0003]">{{ diyComponent.field.required ? '*' : '' }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common';
const props = defineProps(['component', 'index','global']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormTable') {
refresh();
}
}
)
}else{
}
});
const refresh = ()=> {
}
//
const verify = () => {
const res = { code: true, message: '' }
// todo diyComponent.value.field.value
return res;
}
//
const reset = () => {
// todo diyComponent.value.field.value = '';
}
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
.text-overflow-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

View File

@ -0,0 +1,209 @@
<template>
<view v-if="diyComponent.viewFormDetail" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="detail-one-content">
<text class="detail-one-content-label">{{ diyComponent.field.name }}</text>
<text class="detail-one-content-value">{{ diyComponent.field.value }}</text>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<view class="detail-two-content">
<view class="">{{ diyComponent.field.name }}</view>
<view class="detail-two-content-value w-[80%]">{{ diyComponent.field.value }}</view>
</view>
</view>
</view>
<view v-else :style="warpCss" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="layout-one-label">
<text class="text-overflow-ellipsis" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="is-hidden">{{ t('hidden') }}</text>
</view>
<view v-if="diyComponent.field.remark.text" class="layout-one-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-one-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view class="layout-one-content !py-[20rpx] !h-[auto]">
<textarea class="w-[100%]" :placeholder="inputPlaceholder" placeholderClass="layout-one-input-placeholder" :placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx'}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx', 'height': textareaHeight}" v-model="diyComponent.field.value" :disabled="isDisabled" @linechange="linechangeFn" maxlength="500"/>
</view>
<view class="layout-one-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-one-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="layout-two-is-hidden">{{ t('hidden') }}</text>
<view class="layout-two-wrap" :class="{'no-border': !diyGlobal.borderControl}">
<view class="layout-two-label" :class="{'justify-start': diyGlobal.completeAlign == 'left', 'justify-end': diyGlobal.completeAlign == 'right'}">
<text class="required" v-if="diyComponent.field.required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
</view>
<viwe class="layout-two-content">
<textarea class="w-[100%]" :placeholder="inputPlaceholder" placeholderClass="layout-one-input-placeholder" :placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx' }" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx', 'height': textareaHeight}" v-model="diyComponent.field.value" :disabled="isDisabled" @linechange="linechangeFn" maxlength="500"/>
</viwe>
</view>
<view class="layout-two-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view v-if="diyComponent.field.remark.text" class="layout-two-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-two-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-two-attribute-item">{{item.title}}</view>
</view>
</view>
<view v-if="diyStore.mode == 'decorate'" class="form-item-mask"></view>
<form-privacy-pop ref="formPrivacyRef" :data="formPrivacyData" />
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted,nextTick } from 'vue';
import { img } from '@/utils/common';
import { t } from '@/locale'
import formPrivacyPop from './../form-privacy-pop/index.vue';
import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index', 'global']);
const diyStore = useDiyStore();
const errorInfo:any = ref(null);
const formPrivacyRef: any = ref(null);
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const diyGlobal = computed(() => {
return props.global;
})
const inputPlaceholder = computed(() => {
let str = '';
str += diyComponent.value.placeholder
return str;
})
const formPrivacyData = computed(() => {
let str = `${diyComponent.value.field.name}已开启隐私保护,提交后会部分打码,只有你自己和管理员才能查看完整信息`;
return str;
})
// input
const inputAttribute = ()=>{
let arr = [];
if(diyComponent.value.autofill){
let obj = {
title: '已自动填充'
};
arr.push(obj);
}
if(diyComponent.value.field.privacyProtection){
let obj = {
title: '已开启隐私保护',
type: 'privacy'
};
arr.push(obj);
}
arr.forEach((item,index,arr)=>{
if(index != (arr.length-1)){
let obj = {
title: '|'
};
arr.push(obj);
}
})
return arr;
}
const eventFn = (type:any)=>{
if(type == 'privacy'){
//
formPrivacyRef.value.open();
}
}
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
else if (diyComponent.value.componentEndBgColor) style += 'background-color:' + diyComponent.value.componentEndBgColor + ';';
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormTextarea') {
refresh();
}
}
)
}else{
}
});
const refresh = ()=> {
//
if (diyStore.mode == 'decorate') {
diyComponent.value.field.value = diyComponent.value.field.default;
}else {
//
if (diyComponent.value.field.value == '' && diyComponent.value.field.default) {
diyComponent.value.field.value = diyComponent.value.field.default;
}
}
}
const textareaHeight = ref('38rpx')
const linechangeFn = (e)=>{
let height = e.detail.height / e.detail.lineCount;
if(diyComponent.value.rowCount > e.detail.lineCount){
textareaHeight.value = e.detail.height ? (e.detail.height * 2 + 'rpx') : '38rpx';
}else if(diyComponent.value.rowCount <= e.detail.lineCount){
textareaHeight.value = height ? (height * diyComponent.value.rowCount * 2 + 'rpx') : '38rpx';
}
}
//
const verify = () => {
const res = { code: true, message: '' }
if (diyComponent.value.field.required && diyComponent.value.field.value == '' && diyStore.mode != 'decorate') {
res.code = false
res.message = `${ inputPlaceholder.value }`;
}
errorInfo.value = res;
return res;
}
//
const reset = () => {
diyComponent.value.field.value = '';
}
const isDisabled = computed(() => {
return diyStore.mode == 'decorate';
});
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
</style>

View File

@ -0,0 +1,354 @@
<template>
<view v-if="diyComponent.viewFormDetail" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="detail-one-content" v-if="diyComponent.field.value.start.date || diyComponent.field.value.end.date">
<text class="detail-one-content-label">{{ diyComponent.field.name }}</text>
<view class="detail-one-content-value">
<text> {{ diyComponent.field.value.start.date }}</text>
<text v-if="diyComponent.field.value.start.date && diyComponent.field.value.end.date"> - </text>
<text>{{ diyComponent.field.value.end.date }}</text>
</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<view class="detail-two-content" v-if="diyComponent.field.value.start.date || diyComponent.field.value.end.date">
<text class="detail-two-content-label">{{ diyComponent.field.name }}</text>
<view class="detail-two-content-value w-[80%]">
<text>{{ diyComponent.field.value.start.date }}</text>
<text v-if="diyComponent.field.value.start.date && diyComponent.field.value.end.date"> - </text>
<text>{{ diyComponent.field.value.end.date }}</text>
</view>
</view>
</view>
</view>
<view v-else :style="warpCss" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="layout-one-label">
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="is-hidden">{{ t('hidden') }}</text>
</view>
<view v-if="diyComponent.field.remark.text" class="layout-one-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-one-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view class="flex items-center">
<view class="layout-one-content flex-1" @click="startDatetime = true">
<view class="nc-iconfont nc-icon-a-shijianV6xx-36 !text-[32rpx] text-[#999] mr-[16rpx]"></view>
<view class="flex-1 text-overflow-ellipsis" :class="{'!text-[#999]' : !diyComponent.defaultControl && !diyComponent.field.value.start.date}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}">
{{ startTime }}
</view>
</view>
<view class="mx-[10rpx]" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}">-</view>
<view class="layout-one-content flex-1" @click="endDatetime = true">
<view class="nc-iconfont nc-icon-a-shijianV6xx-36 !text-[32rpx] text-[#999] mr-[16rpx]"></view>
<view class="flex-1 text-overflow-ellipsis" :class="{'!text-[#999]' : !diyComponent.defaultControl && !diyComponent.field.value.end.date}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}">
{{ endTime }}
</view>
</view>
</view>
<view class="layout-one-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-one-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="layout-two-is-hidden">{{ t('hidden') }}</text>
<view class="layout-two-wrap" :class="{'no-border': !diyGlobal.borderControl}">
<view class="layout-two-label" :class="{'justify-start': diyGlobal.completeAlign == 'left', 'justify-end': diyGlobal.completeAlign == 'right'}">
<text class="required" v-if="diyComponent.field.required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
</view>
<view class="layout-two-content" @click="openCalendar">
<view class="text-overflow-ellipsis flex justify-center" :class="{'!text-[#999]' : !diyComponent.field.value.start.date && !diyComponent.defaultControl}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" @click="startDatetime = true">
{{ startTime }}
</view>
<view class="mx-[10rpx]" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}">-</view>
<view class="text-overflow-ellipsis flex justify-center" :class="{'!text-[#999]' : !diyComponent.field.value.end.date && !diyComponent.defaultControl}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" @click="endDatetime = true">
{{ endTime }}
</view>
<text class="nc-iconfont !text-[#666] !text-[36rpx] nc-icon-youV6xx -mr-[8rpx]"></text>
</view>
</view>
<view class="layout-two-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view v-if="diyComponent.field.remark.text" class="layout-two-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-two-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-two-attribute-item">{{item.title}}</view>
</view>
</view>
<u-datetime-picker :show="startDatetime" v-model="diyComponent.field.value.start.date" mode="time" @cancel="startDatetime=false" @confirm="startTimeConfirm" @close="startDatetime=false" closeOnClickOverlay="true"></u-datetime-picker>
<u-datetime-picker :show="endDatetime" :minHour="endMinHour" :minMinute="endMinMinute" v-model="diyComponent.field.value.end.date" mode="time" @cancel="endTimeCancel" @confirm="endTimeConfirm" @change="endTimeChange" @close="startDatetime=false" closeOnClickOverlay="true"></u-datetime-picker>
<!-- 遮罩层装修使用 -->
<view v-if="diyStore.mode == 'decorate'" class="form-item-mask"></view>
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import { img } from '@/utils/common';
import { t } from '@/locale'
import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index','global']);
const diyStore = useDiyStore();
const startDatetime = ref(false);
const endDatetime = ref(false);
const calendarShow = ref(false);
const errorInfo:any = ref(null);
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const diyGlobal = computed(() => {
return props.global;
})
// input
const inputAttribute = ()=>{
let arr = [];
if(diyComponent.value.autofill){
let obj = {
title: '已自动填充'
};
arr.push(obj);
}
arr.forEach((item,index,arr)=>{
if(index != (arr.length-1)){
let obj = {
title: '|'
};
arr.push(obj);
}
})
return arr;
}
const eventFn = (type:any)=>{}
//
const startTime = computed(() => {
let returnTime = '';
let time = ''
if(diyComponent.value.field.value.start.date){
returnTime = diyComponent.value.field.value.start.date;
time = diyComponent.value.field.value.start.date;
}else{
if(diyComponent.value.start.defaultControl){
if(diyComponent.value.start.timeWay == 'current'){
returnTime = getTimeFn();
time = getTimeFn();
}else if(diyComponent.value.start.timeWay == 'diy'){
returnTime = diyComponent.value.field.default.start.date;
time = diyComponent.value.field.default.start.date;
}
}else{
returnTime = diyComponent.value.start.placeholder;
time = '';
}
}
diyComponent.value.field.value.start.date = time;
diyComponent.value.field.value.start.timestamp = time ? timeInvertSecond(time) : 0;
return returnTime;
})
//
const endTime = computed(() => {
let returnTime = '';
let time = ''
if(diyComponent.value.field.value.end.date){
returnTime = diyComponent.value.field.value.end.date;
time = diyComponent.value.field.value.end.date;
}else{
if(diyComponent.value.end.defaultControl){
if(diyComponent.value.end.timeWay == 'current'){
let now = new Date();
let tenMinutesLater =new Date(now.getTime()+10 *60* 1000);
returnTime = getTimeFn(tenMinutesLater);
time = getTimeFn(tenMinutesLater);
}else if(diyComponent.value.end.timeWay == 'diy'){
returnTime = diyComponent.value.field.default.end.date;
time = diyComponent.value.field.default.end.date;
}
}else{
returnTime = diyComponent.value.end.placeholder;
time = '';
}
}
diyComponent.value.field.value.end.date = time;
diyComponent.value.field.value.end.timestamp = time ? timeInvertSecond(time) : 0;
return returnTime;
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
else if (diyComponent.value.componentEndBgColor) style += 'background-color:' + diyComponent.value.componentEndBgColor + ';';
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormTimeScope') {
refresh();
}
}
)
}
});
const refresh = ()=> {
// todo
// {{ diyComponent.start.defaultControl ? diyComponent.field.value.start : diyComponent.start.placeholder }}
}
//
const verify = () => {
const res = { code: true, message: '' }
if (diyComponent.value.field.required && diyComponent.value.field.value.start.date == '') {
res.code = false
res.message = `请选择${ diyComponent.value.start.placeholder }`;
}else if (diyComponent.value.field.required && diyComponent.value.field.value.end.date == '') {
res.code = false
res.message = `请选择${ diyComponent.value.end.placeholder }`;
}else if (diyComponent.value.field.value.start.timestamp >= diyComponent.value.field.value.end.timestamp && diyComponent.value.field.value.start.timestamp && diyComponent.value.field.value.end.timestamp) {
res.code = false
res.message = `开始时间不能大于等于结束时间`;
}
errorInfo.value = res;
return res;
}
//
const reset = () => {
diyComponent.value.field.value.start.date = '';
diyComponent.value.field.value.start.timestamp = 0;
diyComponent.value.field.value.end.date = '';
diyComponent.value.field.value.end.timestamp = 0;
}
const openCalendar = () =>{
if(diyStore.mode === 'decorate') return;
calendarShow.value = true;
}
const startTimeConfirm = (e) => {
diyComponent.value.field.value.start.date = e.value;
diyComponent.value.field.value.start.timestamp = timeInvertSecond(e.value);
let returnTime = e.value;
let startTime = new Date(`1970-01-01T${e.value}:00`);
startTime.setMinutes(startTime.getMinutes() + 10);
returnTime = getTimeFn(startTime);
diyComponent.value.field.value.end.date = returnTime;
diyComponent.value.field.value.end.timestamp = timeInvertSecond(returnTime);
startDatetime.value = false;
}
const endTimeConfirm = (e)=>{
diyComponent.value.field.value.end.date = e.value;
diyComponent.value.field.value.end.timestamp = timeInvertSecond(e.value);
endDatetime.value = false;
}
//
const endTimeChange = (e)=>{
if(!temporaryEndTime.value) temporaryEndTime.value = diyComponent.value.field.value.end.date;
diyComponent.value.field.value.end.date = e.value;
}
const endTimeCancel = ()=>{
if(temporaryEndTime.value) diyComponent.value.field.value.end.date = temporaryEndTime.value;
temporaryEndTime.value = '';
endDatetime.value = false;
}
//
const endMinHour = computed(() => {
let arr = diyComponent.value.field.value.start.date.split(':')
return Number(arr[0] ? arr[0] : '0');
})
//
let temporaryEndTime:any = ref("");
const endMinMinute = computed({
get: () => {
let startArr = diyComponent.value.field.value.start.date.split(':');
let endArr = diyComponent.value.field.value.end.date.split(':');
let num = startArr[0] == endArr[0] ? startArr[1] : 0;
return Number(num ? num : '0');
},
set: (newValue) => {
}
})
const confirm = (e)=> {
calendarShow.value = false;
}
const getTimeFn = (data:any='')=>{
let now = data ? new Date(data) : new Date();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
let str = `${hours}:${minutes}`;
return str;
}
//
const timeInvertSecond = (time:any)=>{
let arr = time.split(":");
let num = 0;
if(arr[0]){
num += arr[0] * 60 * 60;
}
if(arr[1]){
num += arr[1] * 60;
}
if(arr[2]){
num += arr[2];
}
return num;
}
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
.text-overflow-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.form-item-frame :deep(.u-picker .u-toolbar__wrapper__cancel), .form-item-frame :deep(.u-picker .u-toolbar__wrapper__confirm){
font-size: 28rpx;
}
</style>

View File

@ -0,0 +1,231 @@
<template>
<view v-if="diyComponent.viewFormDetail" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="detail-one-content">
<text class="detail-one-content-label">{{ diyComponent.field.name }}</text>
<text class="detail-one-content-value">{{ diyComponent.field.value }}</text>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<view class="detail-two-content">
<text class="detail-two-content-label">{{ diyComponent.field.name }}</text>
<view class="detail-two-content-value w-[80%]">{{ diyComponent.field.value }}</view>
</view>
</view>
</view>
<view v-else :style="warpCss" class="form-item-frame">
<view class="base-layout-one" v-if="diyGlobal.completeLayout == 'style-1'">
<view class="layout-one-label">
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="is-hidden">{{ t('hidden') }}</text>
</view>
<view v-if="diyComponent.field.remark.text" class="layout-one-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-one-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view class="layout-one-content">
<view class="nc-iconfont nc-icon-a-shijianV6xx-36 !text-[32rpx] text-[#999] mr-[16rpx]"></view>
<view class="flex-1 text-overflow-ellipsis flex" :class="{'!text-[#999]' : !diyComponent.field.value && !diyComponent.defaultControl}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" @click="startDatetime = true">
{{ startTime }}
</view>
</view>
<view class="layout-one-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-one-attribute-item">{{item.title}}</view>
</view>
</view>
<view class="base-layout-two" v-if="diyGlobal.completeLayout == 'style-2'">
<text v-if="diyStore.mode == 'decorate' && diyComponent.isHidden" class="layout-two-is-hidden">{{ t('hidden') }}</text>
<view class="layout-two-wrap" :class="{'no-border': !diyGlobal.borderControl}">
<view class="layout-two-label" :class="{'justify-start': diyGlobal.completeAlign == 'left', 'justify-end': diyGlobal.completeAlign == 'right'}">
<text class="required" v-if="diyComponent.field.required">{{ diyComponent.field.required ? '*' : '' }}</text>
<text class="name" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
</view>
<view class="layout-two-content" @click="openCalendar">
<view class="flex-1 text-overflow-ellipsis flex justify-end " :class="{'!text-[#999]' : !diyComponent.field.value && !diyComponent.defaultControl}" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}" @click="startDatetime = true">
{{ startTime }}
</view>
<text class="nc-iconfont !text-[#666] !text-[36rpx] nc-icon-youV6xx -mr-[8rpx]"></text>
</view>
</view>
<view class="layout-two-error-message" v-if="errorInfo && !errorInfo.code">{{errorInfo.message}}</view>
<view v-if="diyComponent.field.remark.text" class="layout-two-remark" :style="{ color: diyComponent.field.remark.color, fontSize: (diyComponent.field.remark.fontSize * 2 ) + 'rpx' }">{{ diyComponent.field.remark.text }}</view>
<view class="layout-two-attribute-wrap" v-if="inputAttribute().length">
<view v-for="(item,index) in inputAttribute()" :key="index" @click="eventFn(item.type)" class="layout-two-attribute-item">{{item.title}}</view>
</view>
</view>
<u-datetime-picker :show="startDatetime" v-model="diyComponent.field.value" mode="time" @cancel="startDatetime=false" @close="startDatetime=false" @confirm="startTimeConfirm" closeOnClickOverlay="true"></u-datetime-picker>
<!-- 遮罩层装修使用 -->
<view v-if="diyStore.mode == 'decorate'" class="form-item-mask"></view>
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import { img,timeStampTurnTime } from '@/utils/common';
import { t } from '@/locale'
import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index','global']);
const diyStore = useDiyStore();
const startDatetime = ref(false);
const endDatetime = ref(false);
const calendarShow = ref(false);
const errorInfo:any = ref(null);
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const diyGlobal = computed(() => {
return props.global;
})
// input
const inputAttribute = ()=>{
let arr = [];
if(diyComponent.value.autofill){
let obj = {
title: '已自动填充'
};
arr.push(obj);
}
arr.forEach((item,index,arr)=>{
if(index != (arr.length-1)){
let obj = {
title: '|'
};
arr.push(obj);
}
})
return arr;
}
const eventFn = (type:any)=>{
}
//
const startTime = computed(() => {
var time = '';
if(diyComponent.value.field.value){
time = diyComponent.value.field.value;
diyComponent.value.field.value = time;
}else{
if(diyComponent.value.defaultControl){
if(diyComponent.value.timeWay == 'current'){
time = getTimeFn();
}else if(diyComponent.value.timeWay == 'diy'){
time = diyComponent.value.field.default;
}
diyComponent.value.field.value = time;
}else{
time = diyComponent.value.placeholder;
}
}
return time;
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
else if (diyComponent.value.componentEndBgColor) style += 'background-color:' + diyComponent.value.componentEndBgColor + ';';
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormTime') {
refresh();
}
}
)
}
});
const refresh = ()=> {
// todo
// {{ diyComponent.start.defaultControl ? diyComponent.field.value : diyComponent.start.placeholder }}
}
//
const verify = () => {
const res = { code: true, message: '' }
if (diyComponent.value.field.required && diyComponent.value.field.value == '') {
res.code = false
res.message = `请选择${ diyComponent.value.placeholder }`;
}
errorInfo.value = res;
return res;
}
//
const reset = () => {
diyComponent.value.field.value = '';
}
const openCalendar = () =>{
if(diyStore.mode === 'decorate') return;
calendarShow.value = true;
}
const startTimeConfirm = (e)=>{
diyComponent.value.field.value = e.value;
startDatetime.value = false;
}
const confirm = (e)=> {
calendarShow.value = false;
}
const getTimeFn = (data:any='')=>{
let now = data ? new Date(data) : new Date();
let str = '';
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
str = `${hours}:${minutes}`;
return str;
}
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
.text-overflow-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.form-item-frame :deep(.u-picker .u-toolbar__wrapper__cancel), .form-item-frame :deep(.u-picker .u-toolbar__wrapper__confirm){
font-size: 28rpx;
}
</style>

View File

@ -0,0 +1,124 @@
<template>
<view :style="warpCss">
form-video
<view class="relative">
<view class="p-[10rpx] flex items-center ">
<view class="w-[25%] flex items-center">
<text class="text-overflow-ellipsis" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="text-[#ec0003]">{{ diyComponent.field.required ? '*' : '' }}</text>
</view>
<view class="w-[75%] flex justify-center items-center">
<u-upload :fileList="imgListPreview" :disabled="isDisabled" @afterRead="afterRead" @delete="deletePic" multiple :maxCount="diyComponent.limit"/>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common';
import { uploadImage } from '@/app/api/system'
const props = defineProps(['component', 'index','global']);
const diyStore = useDiyStore();
const selectValue = ref([])
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormVideo') {
refresh();
}
}
)
}
});
const isDisabled = computed(() => {
return diyStore.mode === 'decorate';
});
const imgListPreview = computed(() => {
return selectValue.value.map(item => {
return {url: img(item)}
})
})
const afterRead = (event) => {
event.file.forEach(item => {
uploadImage({
filePath: item.url,
name: 'file'
}).then(res => {
if (selectValue.value.length < diyComponent.value.limit ) {
selectValue.value.push(res.data.url)
}
}).catch(() => {
})
})
}
const deletePic = (event)=> {
selectValue.value.splice(event.index, 1)
}
const refresh = ()=> {
}
//
const verify = () => {
const res = { code: true, message: '' }
// todo diyComponent.value.field.value
return res;
}
//
const reset = () => {
// todo diyComponent.value.field.value = '';
}
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
</style>

View File

@ -0,0 +1,96 @@
<template>
<view :style="warpCss">
form-wechat-name
<view class="relative">
<view class="p-[10rpx] flex items-center ">
<view class="w-[27%] mr-[10rpx] flex items-center">
<text class="text-overflow-ellipsis" :style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
<text class="text-[#ec0003]">{{ diyComponent.field.required ? '*' : '' }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted } from 'vue';
import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common';
const props = defineProps(['component', 'index','global']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'FormWechatName') {
refresh();
}
}
)
}else{
}
});
const refresh = ()=> {
}
//
const verify = () => {
const res = { code: true, message: '' }
// todo diyComponent.value.field.value
return res;
}
//
const reset = () => {
// todo diyComponent.value.field.value = '';
}
defineExpose({
verify,
reset
})
</script>
<style lang="scss" scoped>
@import '@/styles/diy_form.scss';
.text-overflow-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

View File

@ -167,7 +167,7 @@
import { img } from '@/utils/common'; import { img } from '@/utils/common';
import useDiyStore from '@/app/stores/diy'; import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']); const props = defineProps(['component', 'index']);
const diyStore = useDiyStore(); const diyStore = useDiyStore();
@ -226,13 +226,6 @@
return style; return style;
} }
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
const swiperIndex = ref(0); const swiperIndex = ref(0);
const swiperChange = e => { const swiperChange = e => {

View File

@ -7,7 +7,7 @@
import { computed, watch } from 'vue'; import { computed, watch } from 'vue';
import useDiyStore from '@/app/stores/diy'; import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']); const props = defineProps(['component', 'index']);
const diyStore = useDiyStore(); const diyStore = useDiyStore();
@ -33,12 +33,6 @@
return style; return style;
}) })
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
</script> </script>
<style></style> <style></style>

View File

@ -11,7 +11,7 @@
import { computed, watch } from 'vue'; import { computed, watch } from 'vue';
import useDiyStore from '@/app/stores/diy'; import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']); const props = defineProps(['component', 'index']);
const diyStore = useDiyStore(); const diyStore = useDiyStore();
@ -29,12 +29,6 @@
return style; return style;
}) })
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
</script> </script>
<style></style> <style></style>

View File

@ -25,7 +25,7 @@
import useDiyStore from '@/app/stores/diy'; import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common'; import { img } from '@/utils/common';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']); const props = defineProps(['component', 'index']);
const diyStore = useDiyStore(); const diyStore = useDiyStore();
@ -79,12 +79,6 @@
} }
} }
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -28,7 +28,7 @@
import useDiyStore from '@/app/stores/diy'; import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common'; import { img } from '@/utils/common';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']); const props = defineProps(['component', 'index']);
const systemInfo = uni.getSystemInfoSync(); const systemInfo = uni.getSystemInfoSync();
const diyStore = useDiyStore(); const diyStore = useDiyStore();
@ -86,13 +86,6 @@
return style; return style;
}) })
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
const imgHeight = computed(() => { const imgHeight = computed(() => {
return (diyComponent.value.imageHeight * 2) + 'rpx'; return (diyComponent.value.imageHeight * 2) + 'rpx';
}) })

View File

@ -72,7 +72,7 @@
import useDiyStore from '@/app/stores/diy' import useDiyStore from '@/app/stores/diy'
import useConfigStore from '@/stores/config' import useConfigStore from '@/stores/config'
const props = defineProps(['component', 'index', 'pullDownRefreshCount','global']); const props = defineProps(['component', 'index','global']);
const configStore = useConfigStore() const configStore = useConfigStore()
const diyStore = useDiyStore(); const diyStore = useDiyStore();
@ -103,13 +103,6 @@
return style; return style;
}) })
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
const memberStore = useMemberStore() const memberStore = useMemberStore()
// #ifdef H5 // #ifdef H5
@ -157,9 +150,12 @@
uni.showToast({ title: '商家未开启登录注册', icon: 'none' }) uni.showToast({ title: '商家未开启登录注册', icon: 'none' })
} else if (configStore.login.is_username || configStore.login.is_mobile || configStore.login.is_bind_mobile) { } else if (configStore.login.is_username || configStore.login.is_mobile || configStore.login.is_bind_mobile) {
useLogin().setLoginBack({ url: '/app/pages/member/index' }) useLogin().setLoginBack({ url: '/app/pages/member/index' })
} else if (normalLogin && configStore.login.is_auth_register) { } else if (normalLogin && configStore.login.is_auth_register && configStore.login.is_force_access_user_info) {
// //
useLogin().getAuthCode({ scopes: 'snsapi_userinfo' }) useLogin().getAuthCode({ scopes: 'snsapi_userinfo' })
} else if (normalLogin && configStore.login.is_auth_register && !configStore.login.is_force_access_user_info) {
//
useLogin().getAuthCode({ scopes: 'snsapi_base' })
} }
} else { } else {
// //
@ -176,11 +172,18 @@
uni.showToast({ title: '商家未开启登录注册', icon: 'none' }) uni.showToast({ title: '商家未开启登录注册', icon: 'none' })
} else if (configStore.login.is_username || configStore.login.is_mobile || configStore.login.is_bind_mobile) { } else if (configStore.login.is_username || configStore.login.is_mobile || configStore.login.is_bind_mobile) {
useLogin().setLoginBack({ url: '/app/pages/member/index' }) useLogin().setLoginBack({ url: '/app/pages/member/index' })
} else if (normalLogin && configStore.login.is_auth_register) { } else if (normalLogin && configStore.login.is_auth_register && !configStore.login.is_force_access_user_info) {
// //
useLogin().getAuthCode() useLogin().getAuthCode()
} else if (configStore.login.is_auth_register && configStore.login.is_force_access_user_info) {
//
useLogin().setLoginBack({ url: '/app/pages/member/index' })
} else if (configStore.login.is_auth_register && configStore.login.is_bind_mobile) {
//
useLogin().setLoginBack({ url: '/app/pages/member/index' })
} }
// #endif // #endif
} }
const infoFill: any = ref(false) const infoFill: any = ref(false)

View File

@ -112,7 +112,7 @@
import { t } from '@/locale' import { t } from '@/locale'
import useDiyStore from '@/app/stores/diy' import useDiyStore from '@/app/stores/diy'
const props = defineProps(['component', 'index', 'pullDownRefreshCount']); const props = defineProps(['component', 'index']);
const diyStore = useDiyStore(); const diyStore = useDiyStore();
const memberStore = useMemberStore() const memberStore = useMemberStore()
@ -133,13 +133,6 @@
return style; return style;
}) })
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
// //
const upgradeGrowth = ref(0) // const upgradeGrowth = ref(0) //
const currIndex = ref(0) // const currIndex = ref(0) //
@ -157,9 +150,10 @@
growth: 5 growth: 5
} }
} else { } else {
return wap_member_info.value||{}; return memberStore.info || {}
} }
}) })
const list:any = computed(() => { const list:any = computed(() => {
// //
if (diyStore.mode == 'decorate') { if (diyStore.mode == 'decorate') {
@ -170,11 +164,12 @@
} }
}) })
const getMemberLevelFn = (list:any)=> { const getMemberLevelFn = (list:any)=> {
if (!list || !list.length) return false; if (!list || !list.length) return false;
let isSet = false; let isSet = false;
// //
if (info.value && list && list.length) { if (info.value && info.value.member_level && list && list.length) {
list.forEach((item: any, index: any) => { list.forEach((item: any, index: any) => {
if (item.level_id == info.value.member_level) { if (item.level_id == info.value.member_level) {
currIndex.value = index + 1; currIndex.value = index + 1;
@ -206,7 +201,7 @@
} else { } else {
// //
info.value.member_level_name = list[0].level_name; info.value.member_level_name = list[0].level_name;
upgradeGrowth.value = list[0].growth - info.value.growth; upgradeGrowth.value = list[0].growth - (info.value.growth || 0);
afterCurrIndex.value = 0; afterCurrIndex.value = 0;
currIndex.value = 1; currIndex.value = 1;
} }

View File

@ -62,7 +62,7 @@ import { ref,computed, watch, onMounted, nextTick,getCurrentInstance } from 'vue
import { img } from '@/utils/common'; import { img } from '@/utils/common';
import useDiyStore from '@/app/stores/diy'; import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']); const props = defineProps(['component', 'index']);
const diyStore = useDiyStore(); const diyStore = useDiyStore();
const noticeShow = ref(false); const noticeShow = ref(false);
const noticeContent = ref(''); const noticeContent = ref('');
@ -112,13 +112,6 @@ const maskLayer = computed(()=>{
return style; return style;
}); });
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
const marqueeBodyWidth = ref(0); // const marqueeBodyWidth = ref(0); //
const marqueeOneWidth = ref(0); // const marqueeOneWidth = ref(0); //
const marqueeStyle = ref(''); // const marqueeStyle = ref(''); //

View File

@ -42,7 +42,7 @@
import { img } from '@/utils/common'; import { img } from '@/utils/common';
import useDiyStore from '@/app/stores/diy'; import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index', 'pullDownRefreshCount', 'global', 'scrollBool']); const props = defineProps(['component', 'index', 'global', 'scrollBool']);
const diyStore = useDiyStore(); const diyStore = useDiyStore();
const diyComponent = computed(() => { const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') { if (diyStore.mode == 'decorate') {
@ -105,13 +105,6 @@
return style; return style;
} }
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
onMounted(() => { onMounted(() => {
refresh(); refresh();
// //

View File

@ -38,7 +38,7 @@
import useDiyStore from '@/app/stores/diy'; import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common'; import { img } from '@/utils/common';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']); const props = defineProps(['component', 'index']);
const diyStore = useDiyStore(); const diyStore = useDiyStore();
@ -87,13 +87,6 @@
return style; return style;
}); });
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
onMounted(() => { onMounted(() => {
refresh(); refresh();
// //

View File

@ -78,7 +78,7 @@
import useDiyStore from '@/app/stores/diy'; import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common'; import { img } from '@/utils/common';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']); const props = defineProps(['component', 'index']);
const diyStore = useDiyStore(); const diyStore = useDiyStore();
@ -189,13 +189,6 @@
return obj; return obj;
} }
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
onMounted(() => { onMounted(() => {
refresh(); refresh();
// //

View File

@ -43,7 +43,7 @@
import useDiyStore from '@/app/stores/diy'; import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common'; import { img } from '@/utils/common';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']); const props = defineProps(['component', 'index']);
const diyStore = useDiyStore(); const diyStore = useDiyStore();
const diyComponent = computed(() => { const diyComponent = computed(() => {
@ -91,13 +91,6 @@
return style; return style;
}); });
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
onMounted(() => { onMounted(() => {
refresh(); refresh();
// //

View File

@ -1,15 +0,0 @@
<template>
<view>
固定模板示例我也可以装修
<!-- 自定义模板渲染 -->
<diy-group :data="props.data" :pullDownRefreshCount="props.pullDownRefreshCount"></diy-group>
</view>
</template>
<script setup lang="ts">
import { computed, watch } from 'vue';
import diyGroup from '@/addon/components/diy/group/index.vue'
const props = defineProps(['data', 'pullDownRefreshCount']);
</script>
<style></style>

View File

@ -1,7 +1,9 @@
{ {
"alipayAccountNo": "支付宝账号", "alipayAccountNo": "支付宝收款码",
"wechatCode":"微信收款码",
"addBankCard": "添加银行卡", "addBankCard": "添加银行卡",
"addAlipayAccount": "添加支付宝账号", "addWechatCode":"添加微信收款码",
"addAlipayAccount": "添加支付宝收款码",
"endNumber": "尾号", "endNumber": "尾号",
"bankCard": "银行卡" "bankCard": "银行卡"
} }

View File

@ -5,10 +5,14 @@
"addBankCardTips": "请添加持卡人本人的银行卡", "addBankCardTips": "请添加持卡人本人的银行卡",
"editBankCard": "编辑银行卡", "editBankCard": "编辑银行卡",
"editBankCardTips": "请编辑持卡人本人的银行卡", "editBankCardTips": "请编辑持卡人本人的银行卡",
"addAlipayAccount": "添加支付宝账号", "addAlipayAccount": "添加支付宝收款码",
"addAlipayAccountTips": "请添加已实名的支付宝账号", "addAlipayAccountTips": "请添加已实名的支付宝账号",
"editAlipayAccount": "编辑支付宝账号", "editAlipayAccount": "编辑支付宝收款码",
"editAlipayAccountTips": "请编辑已实名的支付宝账号", "editAlipayAccountTips": "请编辑已实名的支付宝账号",
"addWechatCodeAccount": "添加微信收款码",
"addWechatCodeAccountTips": "请添加已实名的微信账号",
"editWechatCodeAccount": "编辑微信收款码",
"editWechatCodeAccountTips": "请编辑已实名的微信账号",
"bankRealname": "持卡人姓名", "bankRealname": "持卡人姓名",
"bankRealnamePlaceholder": "请输入持卡人姓名", "bankRealnamePlaceholder": "请输入持卡人姓名",
"bankName": "银行名称", "bankName": "银行名称",
@ -19,5 +23,9 @@
"alipayRealnamePlaceholder": "请输入真实姓名", "alipayRealnamePlaceholder": "请输入真实姓名",
"alipayAccountNo": "支付宝账号", "alipayAccountNo": "支付宝账号",
"alipayAccountNoPlaceholder": "请输入支付宝账号", "alipayAccountNoPlaceholder": "请输入支付宝账号",
"wechatCodeAccountNo": "微信账号",
"wechatCodeAccountNoPlaceholder": "请输入微信账号",
"alipayAccountImgPlaceholder": "请上传支付宝收款码",
"wechatCodeAccountImgPlaceholder": "请上传微信收款码",
"deleteConfirm": "确定要删除该账号吗?" "deleteConfirm": "确定要删除该账号吗?"
} }

View File

@ -10,13 +10,16 @@
"minWithdrawal": "最小提现金额为", "minWithdrawal": "最小提现金额为",
"commissionTo": "手续费为", "commissionTo": "手续费为",
"cashOutList": "提现记录", "cashOutList": "提现记录",
"cashOutToWechat": "提现至微信", "cashOutToWechat": "提现至微信零钱",
"cashOutToWechatTips": "提现至微信零钱", "cashOutToWechatTips": "提现至微信零钱",
"cashOutToAlipay": "提现至支付宝", "cashOutToAlipay": "提现至支付宝",
"cashOutToAlipayTips": "请先添加支付宝账号", "cashOutToAlipayTips": "请先添加支付宝账号",
"cashOutToBank": "提现至银行卡", "cashOutToBank": "提现至银行卡",
"cashOutToBankTips": "请先添加银行卡", "cashOutToBankTips": "请先添加银行卡",
"cashOutToWechatCode": "提现至微信",
"cashOutToWechatCodeTips": "请先添加微信号",
"alipayAccountNo": "支付宝账号", "alipayAccountNo": "支付宝账号",
"wechatCodeAccountNo": "微信号",
"debitCard": "储蓄卡", "debitCard": "储蓄卡",
"abnormalOperation": "异常操作", "abnormalOperation": "异常操作",
"noAvailableCashOutType": "没有可用的提现方式", "noAvailableCashOutType": "没有可用的提现方式",

View File

@ -1,12 +1,22 @@
{ {
"statusName": "当前状态", "statusName": "当前状态",
"cashOutNo": "交易号", "cashOutNo": "提现单号",
"serviceMoney": "手续费", "serviceMoney": "手续费",
"createTime": "申请时间", "createTime": "申请时间",
"auditTime": "审核时间", "auditTime": "审核时间",
"transferBank": "银行名称", "transferBank": "银行名称",
"transferAccount": "收款账号", "transferAccount": "收款账号",
"refuseReason": "拒绝理由", "refuseReason": "拒绝理由",
"transferTypeName": "转账方式名称", "transferTypeName": "转账方式",
"transferTime": "转账时间" "transferTime": "转账时间",
"transferVoucher":"付款凭证",
"transferRemark":"转账补充说明",
"realname":"真实姓名",
"bankRealname":"持卡人姓名",
"transferCode":"收款码",
"proceedsInfo":"收款方信息",
"transferInfo":"转账信息",
"transferNickname":"收款方昵称",
"transferImg":"收款方头像",
"transferNo":"转账单号"
} }

View File

@ -20,7 +20,7 @@
<view class="mt-[181rpx]"> <view class="mt-[181rpx]">
<!-- #ifdef H5 --> <!-- #ifdef H5 -->
<!-- 微信公众号快捷登录 --> <!-- 微信公众号快捷登录开启自动注册的情况下才能使用 -->
<view v-if="isWeixinBrowser() && loginConfig.is_auth_register" class="w-full flex items-center justify-center mb-[40rpx]"> <view v-if="isWeixinBrowser() && loginConfig.is_auth_register" class="w-full flex items-center justify-center mb-[40rpx]">
<button class="w-[630rpx] h-[88rpx] !mx-[0] !bg-[var(--primary-color)] text-[26rpx] rounded-[44rpx] leading-[88rpx] font-500 !text-[#fff]" @click="oneClickLogin()">{{t('quickLoginOrLogout')}}</button> <button class="w-[630rpx] h-[88rpx] !mx-[0] !bg-[var(--primary-color)] text-[26rpx] rounded-[44rpx] leading-[88rpx] font-500 !text-[#fff]" @click="oneClickLogin()">{{t('quickLoginOrLogout')}}</button>
</view> </view>
@ -32,8 +32,8 @@
<!-- 优先显示第三方登录/注册 --> <!-- 优先显示第三方登录/注册 -->
<view class="w-full flex items-center justify-center mb-[40rpx]" v-if="loginConfig.is_auth_register"> <view class="w-full flex items-center justify-center mb-[40rpx]" v-if="loginConfig.is_auth_register">
<!-- 开启强制绑定手机号或者手机号登录的情况 --> <!-- 开启强制绑定手机号或者手机号登录的情况排除强制获取用户信息的情况is_force_access_user_info为0 -->
<button v-if="!wapMemberMobile && loginConfig.is_bind_mobile" <button v-if="!wapMemberMobile && loginConfig.is_bind_mobile && !loginConfig.is_force_access_user_info"
class="w-[630rpx] h-[88rpx] !bg-[var(--primary-color)] !mx-[0] text-[26rpx] rounded-[44rpx] leading-[88rpx] font-500 !text-[#fff]" class="w-[630rpx] h-[88rpx] !bg-[var(--primary-color)] !mx-[0] text-[26rpx] rounded-[44rpx] leading-[88rpx] font-500 !text-[#fff]"
:open-type="openType" @getphonenumber="mobileAuth" @click="checkWxPrivacy">{{t('quickLoginOrLogout')}}</button> :open-type="openType" @getphonenumber="mobileAuth" @click="checkWxPrivacy">{{t('quickLoginOrLogout')}}</button>
@ -44,7 +44,7 @@
<!-- 未开启第三方登录/注册但是开启了手机号登录则一键手机号登录/注册 --> <!-- 未开启第三方登录/注册但是开启了手机号登录则一键手机号登录/注册 -->
<view class="w-full flex items-center justify-center mb-[40rpx]" v-else-if="!loginConfig.is_auth_register && loginConfig.is_mobile"> <view class="w-full flex items-center justify-center mb-[40rpx]" v-else-if="!loginConfig.is_auth_register && loginConfig.is_mobile">
<button v-if="!wapMemberMobile" class="w-[630rpx] h-[88rpx] !bg-[var(--primary-color)] !mx-[0] text-[26rpx] rounded-[44rpx] leading-[88rpx] font-500 !text-[#fff]" :open-type="openType" @getphonenumber="mobileAuth" @click="checkWxPrivacy">{{t('quickLoginOrLogout')}}</button> <button v-if="!wapMemberMobile" class="w-[630rpx] h-[88rpx] !bg-[var(--primary-color)] !mx-[0] text-[26rpx] rounded-[44rpx] leading-[88rpx] font-500 !text-[#fff]" :open-type="openType" @getphonenumber="mobileAuth" @click="checkWxPrivacy('mobileAuth')">{{t('quickLoginOrLogout')}}</button>
<button v-else class="w-[630rpx] h-[88rpx] !bg-[var(--primary-color)] !mx-[0] text-[26rpx] rounded-[44rpx] leading-[88rpx] font-500 !text-[#fff]" @click="oneClickLogin()">{{t('quickLoginOrLogout')}}</button> <button v-else class="w-[630rpx] h-[88rpx] !bg-[var(--primary-color)] !mx-[0] text-[26rpx] rounded-[44rpx] leading-[88rpx] font-500 !text-[#fff]" @click="oneClickLogin()">{{t('quickLoginOrLogout')}}</button>
</view> </view>
@ -112,6 +112,13 @@
</view> </view>
</view> </view>
</uni-popup> </uni-popup>
<!-- #ifdef MP-WEIXIN -->
<information-filling ref="infoFill"></information-filling>
<!-- #endif -->
<!-- 强制绑定手机号 -->
<bind-mobile ref="bindMobileRef" />
</view> </view>
</template> </template>
@ -125,6 +132,7 @@
import { onLoad,onShow } from '@dcloudio/uni-app' import { onLoad,onShow } from '@dcloudio/uni-app'
import { topTabar } from '@/utils/topTabbar' import { topTabar } from '@/utils/topTabbar'
import useSystemStore from '@/stores/system' import useSystemStore from '@/stores/system'
import { getMobile } from '@/app/api/member'
let menuButtonInfo: any = {}; let menuButtonInfo: any = {};
// (API) // (API)
@ -163,16 +171,26 @@
}); });
const loginLoading = ref(false) const loginLoading = ref(false)
const infoFill: any = ref(false)
const popupRef = ref() const popupRef = ref()
const dialogClose =()=>{ const dialogClose =()=>{
popupRef.value.close(); popupRef.value.close();
} }
const dialogConfirm =()=> { const dialogConfirm =()=> {
isAgree.value = true isAgree.value = true
popupRef.value.close(); popupRef.value.close();
oneClickLogin() oneClickLogin()
} }
//
const bindMobileRef: any = ref(null)
const bindMobileFn = () =>{
bindMobileRef.value.open()
}
onLoad(async ()=> { onLoad(async ()=> {
await systemStore.getSiteInfoFn() await systemStore.getSiteInfoFn()
await configStore.getLoginConfig() await configStore.getLoginConfig()
@ -230,17 +248,21 @@
}) })
// //
const checkWxPrivacy = ()=> { const checkWxPrivacy = (status: any = '')=> {
if (!isAgree.value && configStore.login.agreement_show) { if (!isAgree.value && configStore.login.agreement_show) {
//
if (status) {
uni.showToast({ title: t('isAgreeTips'), icon: 'none' })
} else {
popupRef.value.open(); popupRef.value.open();
// uni.showToast({ title: t('isAgreeTips'), icon: 'none' }) }
return true; return true;
} }
return false; return false;
} }
// //
const oneClickLogin = (callback:any = null)=> { const oneClickLogin = (callback:any = null,data:any = null)=> {
if (checkWxPrivacy()) return; if (checkWxPrivacy()) return;
if (loginLoading.value) return if (loginLoading.value) return
@ -258,21 +280,41 @@
// //
// #ifdef MP // #ifdef MP
weappLogin(callback) weappLogin(callback, data)
// #endif // #endif
} }
// //
const wechatLogin = ()=> { const wechatLogin = ()=> {
if (isWeixinBrowser()) { if (isWeixinBrowser()) {
login.getAuthCode({ scopes : 'snsapi_userinfo' }) let loginConfig = uni.getStorageSync('login_config')
if (loginConfig.is_auth_register) {
//
if (loginConfig.is_bind_mobile) {
bindMobileFn();
} else if (loginConfig.is_force_access_user_info) {
//
login.getAuthCode({ scopes: 'snsapi_userinfo' }) //
} else if (!loginConfig.is_force_access_user_info) {
//
login.getAuthCode({ scopes: 'snsapi_base' }) //
}
}
loginLoading.value = false loginLoading.value = false
} }
} }
// //
const weappLogin = (successCallback: any)=> { const weappLogin = (successCallback: any,data: any)=> {
login.getAuthCode({ backFlag: true, successCallback }) let loginConfig = uni.getStorageSync('login_config')
let member_exist = uni.getStorageSync('member_exist')
if(loginConfig.is_auth_register && loginConfig.is_force_access_user_info && !member_exist) {
infoFill.value.show = true
loginLoading.value = false
}else {
data = data || {};
login.getAuthCode({ backFlag: true, successCallback, ...data })
}
} }
const agreeChange = () => { const agreeChange = () => {
@ -288,7 +330,7 @@
uni.setStorageSync('wap_member_mobile', memberInfo.value.mobile) // uni.setStorageSync('wap_member_mobile', memberInfo.value.mobile) //
} }
loginLoading.value = false loginLoading.value = false
}); }, { mobile_code: e.detail.code });
} }
if (e.detail.errno == 104) { if (e.detail.errno == 104) {

View File

@ -98,7 +98,7 @@
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed, onMounted,nextTick } from 'vue' import { ref, reactive, computed, onMounted } from 'vue'
import { usernameLogin, mobileLogin } from '@/app/api/auth' import { usernameLogin, mobileLogin } from '@/app/api/auth'
import useMemberStore from '@/stores/member' import useMemberStore from '@/stores/member'
import useConfigStore from '@/stores/config' import useConfigStore from '@/stores/config'
@ -128,17 +128,21 @@
const isShowQuickLogin = ref(false) // const isShowQuickLogin = ref(false) //
const popupRef = ref() const popupRef = ref()
const isPassword = ref(true) const isPassword = ref(true)
const changePassword =()=>{ const changePassword =()=>{
isPassword.value = !isPassword.value isPassword.value = !isPassword.value
} }
const dialogClose =()=>{ const dialogClose =()=>{
popupRef.value.close(); popupRef.value.close();
} }
const dialogConfirm =()=>{ const dialogConfirm =()=>{
isAgree.value=true isAgree.value=true
popupRef.value.close(); popupRef.value.close();
handleLogin() handleLogin()
} }
onLoad(async (option: any)=> { onLoad(async (option: any)=> {
await configStore.getLoginConfig() await configStore.getLoginConfig()
if (!getToken() && !configStore.login.is_username && !configStore.login.is_mobile) { if (!getToken() && !configStore.login.is_username && !configStore.login.is_mobile) {

View File

@ -26,7 +26,7 @@
<view class="px-[20rpx] box-border"> <view class="px-[20rpx] box-border">
<button class="bg-[#FFB4B1] !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500" hover-class="none" v-if="friendsInfo.status == 2 ">{{ t('finish') }}</button> <button class="bg-[#FFB4B1] !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500" hover-class="none" v-if="friendsInfo.status == 2 ">{{ t('finish') }}</button>
<button class="bg-[#FFB4B1] !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500" hover-class="none" v-else-if="friendsInfo.status == -1">{{ t('close') }}</button> <button class="bg-[#FFB4B1] !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500" hover-class="none" v-else-if="friendsInfo.status == -1">{{ t('close') }}</button>
<button class="primary-btn-bg !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500" hover-class="none" v-else :loading="operateLoading" @click="save">{{ friendsInfo.config.pay_button_name ? friendsInfo.config.pay_button_name : t('payGenerously') }}</button> <button class="botton-color !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500" hover-class="none" v-else :loading="operateLoading" @click="save">{{ friendsInfo.config.pay_button_name ? friendsInfo.config.pay_button_name : t('payGenerously') }}</button>
</view> </view>
<view class="mt-[20rpx] flex items-baseline justify-center text-[var(--text-color-light9)]" @click="redirect({url: '/app/pages/index/index'})"> <view class="mt-[20rpx] flex items-baseline justify-center text-[var(--text-color-light9)]" @click="redirect({url: '/app/pages/index/index'})">
<text class="text-[24rpx] mr-[6rpx]">返回首页</text> <text class="text-[24rpx] mr-[6rpx]">返回首页</text>
@ -214,4 +214,7 @@ const save = () =>{
transform: translateX(-50%) translateY(-50%) rotate(45deg); transform: translateX(-50%) translateY(-50%) rotate(45deg);
} }
} }
.botton-color{
background: linear-gradient( 94deg, #FB7939 0%, #FE120E 99%), #EF000C;
}
</style> </style>

View File

@ -25,7 +25,7 @@
<view class="px-[20rpx] box-border"> <view class="px-[20rpx] box-border">
<button class="bg-[#FFB4B1] !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500" hover-class="none" v-if="friendsInfo.status == 2">{{ t('finish') }}</button> <button class="bg-[#FFB4B1] !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500" hover-class="none" v-if="friendsInfo.status == 2">{{ t('finish') }}</button>
<button class="bg-[#FFB4B1] !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500" hover-class="none" v-else-if="friendsInfo.status == -1">{{ t('close') }}</button> <button class="bg-[#FFB4B1] !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500" hover-class="none" v-else-if="friendsInfo.status == -1">{{ t('close') }}</button>
<button class="primary-btn-bg !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500" hover-class="none" v-else :loading="operateLoading" @click="openShareFn">{{ friendsInfo.config.pay_type_name ? friendsInfo.config.pay_type_name : t('friendPay') }}</button> <button class="botton-color !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500" hover-class="none" v-else :loading="operateLoading" @click="openShareFn">{{ friendsInfo.config.pay_type_name ? friendsInfo.config.pay_type_name : t('friendPay') }}</button>
</view> </view>
<view class="mt-[20rpx] flex items-baseline justify-center text-[var(--text-color-light9)]" v-if="friendsInfo.status == 2 && JSON.stringify(friendsInfo.trade_info) !== '[]' && friendsInfo.trade_info.detail_url" @click="redirect({url: friendsInfo.trade_info.detail_url })"> <view class="mt-[20rpx] flex items-baseline justify-center text-[var(--text-color-light9)]" v-if="friendsInfo.status == 2 && JSON.stringify(friendsInfo.trade_info) !== '[]' && friendsInfo.trade_info.detail_url" @click="redirect({url: friendsInfo.trade_info.detail_url })">
<text class="text-[24rpx] mr-[6rpx]">查看订单</text> <text class="text-[24rpx] mr-[6rpx]">查看订单</text>
@ -182,7 +182,19 @@ const getFriendspayInfoFn = (tradeType : string, tradeId : number) => {
share.title = `${name}希望你帮他付${friendsInfo.value.money}` share.title = `${name}希望你帮他付${friendsInfo.value.money}`
} }
if(JSON.stringify(friendsInfo.value.trade_info) !== '[]' && friendsInfo.value.trade_info.item_list.length){ if(JSON.stringify(friendsInfo.value.trade_info) !== '[]' && friendsInfo.value.trade_info.item_list.length){
share.url = friendsInfo.value.trade_info.item_list[0].item_image // #ifdef H5
share.url = friendsInfo.value.trade_info.item_list[0].item_image ? friendsInfo.value.trade_info.item_list[0].item_image : friendsInfo.value.config.pay_wechat_share_image
// #endif
// #ifdef MP-WEIXIN
share.url = friendsInfo.value.trade_info.item_list[0].item_image ? friendsInfo.value.trade_info.item_list[0].item_image : friendsInfo.value.config.pay_weapp_share_image
// #endif
}else{
// #ifdef H5
share.url = friendsInfo.value.config.pay_wechat_share_image
// #endif
// #ifdef MP-WEIXIN
share.url = friendsInfo.value.config.pay_weapp_share_image
// #endif
} }
setShare({ setShare({
wechat: { wechat: {
@ -258,4 +270,7 @@ const openShareFn = ()=>{
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.botton-color{
background: linear-gradient( 94deg, #FB7939 0%, #FE120E 99%), #EF000C;
}
</style> </style>

View File

@ -6,16 +6,9 @@
<view v-show="!diy.getLoading()"> <view v-show="!diy.getLoading()">
<!-- 自定义模板渲染 --> <!-- 自定义模板渲染 -->
<view class="diy-template-wrap bg-index" v-if="diy.data.pageMode != 'fixed'" :style="diy.pageStyle()"> <view class="diy-template-wrap bg-index" :style="diy.pageStyle()">
<diy-group ref="diyGroupRef" :data="diy.data" :pullDownRefreshCount="diy.pullDownRefreshCount" /> <diy-group ref="diyGroupRef" :data="diy.data" />
</view>
<!-- 固定模板渲染 -->
<view class="fixed-template-wrap" v-if="diy.data.pageMode == 'fixed'">
<fixed-group :data="diy.data" :pullDownRefreshCount="diy.pullDownRefreshCount" />
</view> </view>
@ -34,7 +27,6 @@
import {useDiy} from '@/hooks/useDiy' import {useDiy} from '@/hooks/useDiy'
import {useShare} from '@/hooks/useShare' import {useShare} from '@/hooks/useShare'
import diyGroup from '@/addon/components/diy/group/index.vue' import diyGroup from '@/addon/components/diy/group/index.vue'
import fixedGroup from '@/addon/components/fixed/group/index.vue'
const {setShare} = useShare() const {setShare} = useShare()
@ -65,9 +57,6 @@
// //
diy.onUnload(); diy.onUnload();
//
diy.onPullDownRefresh()
// //
diy.onPageScroll() diy.onPageScroll()
</script> </script>

View File

@ -0,0 +1,119 @@
<template>
<view :style="themeColor()">
<loading-page :loading="diy.getLoading()"></loading-page>
<view v-show="requestData.status == 1 && requestData.error && requestData.error.length === 0 && !diy.getLoading()">
<!-- 自定义模板渲染 -->
<view class="diy-template-wrap bg-index" :style="diy.pageStyle()">
<diy-group ref="diyGroupRef" :data="diy.data" />
</view>
</view>
<view class="w-screen h-screen flex flex-col " v-if="requestData.error && requestData.error.length > 0">
<view class="flex-1 flex flex-col items-center pt-[180rpx] px-[60rpx]" v-for="(item, index) in requestData.error.slice(0, 1)" :key="index">
<text class="nc-iconfont nc-icon-tanhaoV6mm text-[#ccc] mb-[30rpx] !text-[100rpx]"></text>
<view class="text-[38rpx] font-bold mt-3">{{item.title}}</view>
<view class="p-[30rpx] mt-10 w-full ">
<view class="flex w-full">
<view class="w-[30%] text-[#999] text-left">{{item.type}}</view>
<view class="w-[70%] text-left">{{item.desc}}</view>
</view>
</view>
</view>
<view class="pb-[260rpx]" >
<button class="w-[380rpx] !border-0 h-[80rpx] text-[28rpx] text-[#333] !bg-[#f2f2f2] flex-center font-500 rounded-[20rpx]" :plain="true" @click="finishFn">{{ t('close') }}</button>
</view>
</view>
<!-- #ifdef MP-WEIXIN -->
<!-- 小程序隐私协议 -->
<wx-privacy-popup ref="wxPrivacyPopupRef"></wx-privacy-popup>
<!-- #endif -->
</view>
</template>
<script setup lang="ts">
import {ref,nextTick,computed} from 'vue';
import {useDiyForm} from '@/hooks/useDiyForm'
import {useShare} from '@/hooks/useShare'
import { img,redirect} from '@/utils/common';
import { t } from '@/locale'
import diyGroup from '@/addon/components/diy/group/index.vue'
import MescrollEmpty from '@/components/mescroll/mescroll-empty/mescroll-empty.vue';
const {setShare} = useShare()
const diy = useDiyForm({
needLogin: true //
})
const diyGroupRef = ref(null)
const wxPrivacyPopupRef:any = ref(null)
const requestData = computed(()=>{
return diy.requestData;
})
const finishFn = () => {
redirect({
url: '/app/pages/index/index',
mode: 'reLaunch'
});
}
diy.onLoad((data:any)=>{
let share = data.share ? data.share : null;
setShare(share);
diyGroupRef.value?.refresh();
// #ifdef MP
nextTick(()=>{
if(wxPrivacyPopupRef.value) wxPrivacyPopupRef.value.proactive();
})
// #endif
});
//
diy.onShow((data: any) => {
let share = data.share ? data.share : null;
if(share) {
setShare(share);
}
diyGroupRef.value?.refresh();
// #ifdef MP
nextTick(()=>{
if(wxPrivacyPopupRef.value) wxPrivacyPopupRef.value.proactive();
})
// #endif
});
//
diy.onHide();
//
diy.onUnload();
//
diy.onPageScroll()
</script>
<style lang="scss" scoped>
@import '@/styles/diy.scss';
</style>
<style lang="scss">
.diy-template-wrap {
/* #ifdef MP */
.child-diy-template-wrap {
::v-deep .diy-group {
> .draggable-element.top-fixed-diy {
display: block !important;
}
}
}
/* #endif */
}
</style>

View File

@ -0,0 +1,28 @@
<template>
<view :style="themeColor()">
<view class="w-screen h-screen bg-[var(--page-bg-color)] min-h-[100vh]">
<view class="bg-white p-3">
<view class="text-[30rpx] font-500 leading-[45rpx]">{{ t('detailInformation') }}</view>
<u-divider text=""></u-divider>
<!-- 动态渲染表单组件详情 -->
<diy-form-detail :record_id="recordId" completeLayout="style-1" />
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { t } from '@/locale'
import diyFormDetail from '@/addon/components/diy-form-detail/index.vue'
const recordId = ref(0)
onLoad((data : any) => {
recordId.value = data.record_id
})
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,83 @@
<template>
<view :style="themeColor()">
<view class="w-screen h-screen flex flex-col items-center" v-if="recordInfo">
<top-tabbar ref="topTabbarRef" :data="topTabbarParam" />
<view class="flex-1 flex flex-col items-center w-full pt-[180rpx]">
<text class="nc-iconfont nc-icon-duihaoV6mm text-[#06ae56] mb-[30rpx] !text-[65rpx]"></text>
<view class="px-[30rpx] text-center leading-[1.3] text-[42rpx] font-bold mb-[30rpx]">
{{ recordInfo.submitConfig.tips_type=='default' ? '填写成功' : recordInfo.submitConfig.tips_text }}
</view>
<view class="text-[32rpx] mt-[32rpx] text-[#576b95]" @click="toDetail()">{{ t('viewFillingDetails') }}</view>
</view>
<view class="pb-[260rpx] action-wrap">
<template v-if="recordInfo.submitConfig.success_after_action.finish">
<button class="w-[380rpx] !border-0 h-[80rpx] text-[28rpx] !text-[#ffffff] !bg-[#20bf64] flex-center font-500 rounded-[6rpx]" :plain="true" @click="finishFn">{{ t('complete') }}</button>
</template>
<template v-if="recordInfo.submitConfig.success_after_action.goback">
<button class="w-[380rpx] !border-0 h-[80rpx] text-[28rpx] text-[#333] !bg-[#f2f2f2] flex-center font-500 rounded-[6rpx]" :plain="true" @click="goback">{{ t('back') }}</button>
</template>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getFormResultInfo } from '@/app/api/diy_form';
import { t } from '@/locale'
import { redirect } from '@/utils/common'
import { topTabar } from '@/utils/topTabbar'
const recordInfo = ref<AnyObject | null>(null)
const recordId = ref(0)
const formId = ref(0)
/********* 自定义头部 - start ***********/
const topTabarObj = topTabar()
let topTabbarParam = topTabarObj.setTopTabbarParam({title:'',isBack:false})
/********* 自定义头部 - end ***********/
onLoad((data : any) => {
recordId.value = data.record_id || 0;
formId.value = data.form_id || 0;
getInfo()
})
/**
* 获取表单填写信息
*/
const getInfo = () => {
getFormResultInfo({
record_id: recordId.value
}).then((res: any) => {
recordInfo.value = res.data
})
}
const toDetail = ()=>{
redirect({ url: '/app/pages/index/diy_form_detail',param: { record_id: recordId.value }, mode: 'redirectTo' })
}
const finishFn = () => {
redirect({
url: '/app/pages/index/index',
mode: 'reLaunch'
});
}
const goback = ()=>{
redirect({ url: '/app/pages/index/diy_form',param: { form_id: formId.value }, mode: 'redirectTo' })
}
</script>
<style lang="scss" scoped>
.action-wrap{
button{
margin-bottom: 30rpx;
&:last-child{
margin-bottom: 0;
}
}
}
</style>

View File

@ -6,16 +6,9 @@
<view v-show="!diy.getLoading()"> <view v-show="!diy.getLoading()">
<!-- 自定义模板渲染 --> <!-- 自定义模板渲染 -->
<view class="diy-template-wrap bg-index" v-if="diy.data.pageMode != 'fixed'" :style="diy.pageStyle()"> <view class="diy-template-wrap bg-index" :style="diy.pageStyle()">
<diy-group ref="diyGroupRef" :data="diy.data" :pullDownRefreshCount="diy.pullDownRefreshCount" /> <diy-group ref="diyGroupRef" :data="diy.data" />
</view>
<!-- 固定模板渲染 -->
<view class="fixed-template-wrap" v-if="diy.data.pageMode == 'fixed'">
<fixed-group :data="diy.data" :pullDownRefreshCount="diy.pullDownRefreshCount" />
</view> </view>
@ -34,7 +27,6 @@
import {useDiy} from '@/hooks/useDiy' import {useDiy} from '@/hooks/useDiy'
import {redirect} from '@/utils/common'; import {redirect} from '@/utils/common';
import diyGroup from '@/addon/components/diy/group/index.vue' import diyGroup from '@/addon/components/diy/group/index.vue'
import fixedGroup from '@/addon/components/fixed/group/index.vue'
uni.hideTabBar() // tabbar uni.hideTabBar() // tabbar
@ -73,9 +65,6 @@
// //
diy.onUnload(); diy.onUnload();
//
diy.onPullDownRefresh()
// //
diy.onPageScroll() diy.onPageScroll()
</script> </script>

View File

@ -13,6 +13,8 @@
import { t } from '@/locale' import { t } from '@/locale'
import useSystemStore from '@/stores/system' import useSystemStore from '@/stores/system'
uni.hideTabBar() // tabbar
const systemStore = useSystemStore() const systemStore = useSystemStore()
watch( watch(

View File

@ -8,10 +8,11 @@
<view class="flex items-center"> <view class="flex items-center">
<view class="w-[80rpx] h-[80rpx] flex items-center justify-center" @click="handleClick(item)"> <view class="w-[80rpx] h-[80rpx] flex items-center justify-center" @click="handleClick(item)">
<image class="w-[80rpx] h-[52rpx] align-middle" :src="img('static/resource/images/member/apply_withdrawal/bank-icon.png')" mode="widthFix" v-if="item.account_type == 'bank'" /> <image class="w-[80rpx] h-[52rpx] align-middle" :src="img('static/resource/images/member/apply_withdrawal/bank-icon.png')" mode="widthFix" v-if="item.account_type == 'bank'" />
<image class="w-[80rpx] h-[52rpx] align-middle" :src="img('static/resource/images/member/apply_withdrawal/wechat_code.png')" mode="widthFix" v-else-if="item.account_type == 'wechat_code'" />
<image class="h-[78rpx] w-[78rpx] align-middle" v-else :src="img('static/resource/images/member/apply_withdrawal/alipay-icon.png')" mode="widthFix" /> <image class="h-[78rpx] w-[78rpx] align-middle" v-else :src="img('static/resource/images/member/apply_withdrawal/alipay-icon.png')" mode="widthFix" />
</view> </view>
<view class="flex flex-col ml-[20rpx]" @click="handleClick(item)"> <view class="flex flex-col ml-[20rpx]" @click="handleClick(item)">
<view class="text-[#333] text-[28rpx]">{{ item.account_type == 'bank' ? item.bank_name : t('alipayAccountNo') }}</view> <view class="text-[#333] text-[28rpx]">{{ item.account_type == 'bank' ? item.bank_name : item.account_type == 'wechat_code'?t('wechatCode') : t('alipayAccountNo') }}</view>
<view v-if="item.account_type == 'bank'" class="text-[var(--text-color-light9)] text-[24rpx] mt-[12rpx]">{{ t('endNumber') }} {{ item.account_no.substring(item.account_no.length - 4) }}{{ t('bankCard') }}</view> <view v-if="item.account_type == 'bank'" class="text-[var(--text-color-light9)] text-[24rpx] mt-[12rpx]">{{ t('endNumber') }} {{ item.account_no.substring(item.account_no.length - 4) }}{{ t('bankCard') }}</view>
<view v-else class="text-[var(--text-color-light9)] text-[24rpx] mt-[12rpx]">{{ item.account_no }}</view> <view v-else class="text-[var(--text-color-light9)] text-[24rpx] mt-[12rpx]">{{ item.account_no }}</view>
</view> </view>
@ -23,7 +24,7 @@
</view> </view>
<view class="card-template sidebar-margin my-[var(--top-m)] flex items-center" @click="redirect({ url: '/app/pages/member/account_edit', param: { type: accountType, mode } })"> <view class="card-template sidebar-margin my-[var(--top-m)] flex items-center" @click="redirect({ url: '/app/pages/member/account_edit', param: { type: accountType, mode } })">
<text class="nc-iconfont nc-icon-jiahaoV6xx text-[30rpx]"></text> <text class="nc-iconfont nc-icon-jiahaoV6xx text-[30rpx]"></text>
<text class="text-[28rpx] ml-[10rpx] flex-1">{{ accountType == 'bank' ? t('addBankCard') : t('addAlipayAccount') }}</text> <text class="text-[28rpx] ml-[10rpx] flex-1">{{ accountType == 'bank' ? t('addBankCard') : accountType == 'wechat_code' ? t('addWechatCode') : t('addAlipayAccount') }}</text>
<text class="nc-iconfont nc-icon-youV6xx text-[24rpx] text-[var(--text-color-light6)]"></text> <text class="nc-iconfont nc-icon-youV6xx text-[24rpx] text-[var(--text-color-light6)]"></text>
</view> </view>
</mescroll-body> </mescroll-body>

View File

@ -28,7 +28,7 @@
<block v-if="formData.account_type == 'alipay'"> <block v-if="formData.account_type == 'alipay'">
<view class="text-center text-[32rpx] font-500 mt-[20rpx] text-[#333] leading-[42rpx]">{{ formData.account_id ? t('editAlipayAccount') : t('addAlipayAccount') }}</view> <view class="text-center text-[32rpx] font-500 mt-[20rpx] text-[#333] leading-[42rpx]">{{ formData.account_id ? t('editAlipayAccount') : t('addAlipayAccount') }}</view>
<view class="text-center text-[28rpx] mt-[16rpx] text-[var(--text-color-light9)] leading-[36rpx]">{{ formData.account_id ? t('editAlipayAccountTips') : t('addAlipayAccountTips') }}</view> <!-- <view class="text-center text-[28rpx] mt-[16rpx] text-[var(--text-color-light9)] leading-[36rpx]">{{ formData.account_id ? t('editAlipayAccountTips') : t('addAlipayAccountTips') }}</view> -->
<view class="mt-[70rpx] px-[10rpx]"> <view class="mt-[70rpx] px-[10rpx]">
<u-form labelPosition="left" :model="formData" labelWidth="200rpx" errorType='toast' :rules="rules" ref="formRef"> <u-form labelPosition="left" :model="formData" labelWidth="200rpx" errorType='toast' :rules="rules" ref="formRef">
@ -41,6 +41,48 @@
<u-form-item :label="t('alipayAccountNo')" prop="account_no"> <u-form-item :label="t('alipayAccountNo')" prop="account_no">
<u-input v-model.trim="formData.account_no" border="none" maxlength="30" fontSize="28rpx" clearable :placeholder="t('alipayAccountNoPlaceholder')"/> <u-input v-model.trim="formData.account_no" border="none" maxlength="30" fontSize="28rpx" clearable :placeholder="t('alipayAccountNoPlaceholder')"/>
</u-form-item> </u-form-item>
</view>
<view class="mt-[16rpx]">
<u-form-item label="收款码" prop="transfer_payment_code">
<view class="relative w-[160rpx] h-[160rpx]" v-if="formData.transfer_payment_code">
<image class="w-[160rpx] h-[160rpx]" :src="img(formData.transfer_payment_code)" mode="aspectFill" @click="previewImageFn(img(formData.transfer_payment_code))"></image>
<view class="absolute top-0 right-0 bg-[#373737] flex justify-end h-[28rpx] w-[28rpx] rounded-bl-[40rpx]" @click="collectionCodeDeleteFn">
<text class="nc-iconfont nc-icon-guanbiV6xx !text-[20rpx] mt-[2rpx] mr-[2rpx] text-[#fff]"></text>
</view>
</view>
<u-upload v-else @afterRead="collectionCodeAfterRead" @delete="collectionCodeDeleteFn" :maxCount="1"></u-upload>
</u-form-item>
</view>
</u-form>
</view>
</block>
<block v-if="formData.account_type == 'wechat_code'">
<view class="text-center text-[32rpx] font-500 mt-[20rpx] text-[#333] leading-[42rpx]">{{ formData.account_id ? t('editWechatCodeAccount'): t('addWechatCodeAccount') }}</view>
<!-- <view class="text-center text-[28rpx] mt-[16rpx] text-[var(--text-color-light9)] leading-[36rpx]">{{ formData.account_id ? t('editWechatCodeAccountTips') : t('addWechatCodeAccountTips') }}</view> -->
<view class="mt-[70rpx] px-[10rpx]">
<u-form labelPosition="left" :model="formData" labelWidth="200rpx" errorType='toast' :rules="rules" ref="formRef">
<view>
<u-form-item :label="t('alipayRealname')" prop="realname">
<u-input v-model.trim="formData.realname" maxlength="30" border="none" fontSize="28rpx" clearable :placeholder="t('alipayRealnamePlaceholder')"/>
</u-form-item>
</view>
<view class="mt-[16rpx]">
<u-form-item :label="t('wechatCodeAccountNo')" prop="account_no">
<u-input v-model.trim="formData.account_no" border="none" maxlength="30" fontSize="28rpx" clearable :placeholder="t('wechatCodeAccountNoPlaceholder')"/>
</u-form-item>
</view>
<view class="mt-[16rpx]">
<u-form-item label="收款码" prop="transfer_payment_code">
<view class="relative w-[160rpx] h-[160rpx]" v-if="formData.transfer_payment_code">
<image class="w-[160rpx] h-[160rpx]" :src="img(formData.transfer_payment_code)" mode="aspectFill" @click="previewImageFn(img(formData.transfer_payment_code))"></image>
<view class="absolute top-0 right-0 bg-[#373737] flex justify-end h-[28rpx] w-[28rpx] rounded-bl-[40rpx]" @click="collectionCodeDeleteFn">
<text class="nc-iconfont nc-icon-guanbiV6xx !text-[20rpx] mt-[2rpx] mr-[2rpx] text-[#fff]"></text>
</view>
</view>
<u-upload v-else @afterRead="collectionCodeAfterRead" @delete="collectionCodeDeleteFn" :maxCount="1"></u-upload>
</u-form-item>
</view> </view>
</u-form> </u-form>
</view> </view>
@ -61,7 +103,8 @@
import { onLoad } from '@dcloudio/uni-app' import { onLoad } from '@dcloudio/uni-app'
import { getCashoutAccountInfo, addCashoutAccount, editCashoutAccount, deleteCashoutAccount } from '@/app/api/member' import { getCashoutAccountInfo, addCashoutAccount, editCashoutAccount, deleteCashoutAccount } from '@/app/api/member'
import { t } from '@/locale' import { t } from '@/locale'
import { redirect } from '@/utils/common' import { uploadImage } from '@/app/api/system'
import { redirect, img } from '@/utils/common'
const loading = ref(false) const loading = ref(false)
const formRef: any = ref(null) const formRef: any = ref(null)
@ -72,7 +115,8 @@
account_type: 'bank', account_type: 'bank',
bank_name: '', bank_name: '',
realname: '', realname: '',
account_no: '' account_no: '',
transfer_payment_code: ''
}) })
const rules = computed(() => { const rules = computed(() => {
@ -89,11 +133,15 @@
message: t('bankNamePlaceholder'), message: t('bankNamePlaceholder'),
trigger: ['blur', 'change'], trigger: ['blur', 'change'],
}, },
'account_no': { 'transfer_payment_code': {
type: 'string', validator(rule, value, callback) {
required: true, if (!value || !value.length){
message: formData.account_type == 'bank' ? t('bankAccountNoPlaceholder') : t('alipayAccountNoPlaceholder'), let tips = formData.account_type == 'alipay' ? t('alipayAccountImgPlaceholder') : t('wechatCodeAccountImgPlaceholder');
trigger: ['blur', 'change'], callback(new Error(tips))
} else {
callback()
}
}
} }
} }
}) })
@ -139,11 +187,37 @@
}) })
} }
const collectionCodeAfterRead = (e)=>{
uploadImage({
filePath: e.file.url,
name: 'file'
}).then(res => {
if(res.data){
formData.transfer_payment_code = '';
formData.transfer_payment_code = res.data.url;
}
}).catch(() => {
})
}
//
const collectionCodeDeleteFn = (e) =>{
formData.transfer_payment_code = '';
}
const handleDelete = () => { const handleDelete = () => {
deleteCashoutAccount(formData.account_id).then(() => { deleteCashoutAccount(formData.account_id).then(() => {
redirect({ url: '/app/pages/member/account', mode: 'redirectTo' }) redirect({ url: '/app/pages/member/account', mode: 'redirectTo' })
}) })
} }
//
const previewImageFn = (url:any) => {
uni.previewImage({
current: 0,
urls: [url]
});
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -18,16 +18,16 @@
<view v-if="!formData.area" class="text-[#888] text-[28rpx] flex-1">{{ t('selectAreaPlaceholder') }}</view> <view v-if="!formData.area" class="text-[#888] text-[28rpx] flex-1">{{ t('selectAreaPlaceholder') }}</view>
<view v-else class="text-[28rpx] flex-1 leading-[1.4]">{{ formData.area }}</view> <view v-else class="text-[28rpx] flex-1 leading-[1.4]">{{ formData.area }}</view>
<view @click.stop="chooseLocation" class="flex items-center"> <view @click.stop="chooseLocation" class="flex items-center">
<text class="nc-iconfont nc-icon-dizhiguanliV6xx mr-[4rpx] text-[32rpx] text-[#e93323]"></text> <text class="nc-iconfont nc-icon-dizhiguanliV6xx mr-[4rpx] text-[32rpx] text-[var(--primary-color)]"></text>
<text class="text-[24rpx] whitespace-nowrap text-[#e93323]">定位</text> <text class="text-[24rpx] whitespace-nowrap text-[var(--primary-color)]">定位</text>
</view> </view>
</view> </view>
<view v-else class="flex justify-between items-center flex-1 h-[52rpx]" @click="chooseLocation"> <view v-else class="flex justify-between items-center flex-1 h-[52rpx]" @click="chooseLocation">
<view class="text-[28rpx] text-[#303133] leading-[1.4]" v-if="formData.area || formData.address_name">{{formData.area || formData.address_name}}</view> <view class="text-[28rpx] text-[#303133] leading-[1.4]" v-if="formData.area || formData.address_name">{{formData.area || formData.address_name}}</view>
<view class="text-[#888] text-[28rpx]" v-else>{{t('selectAddressPlaceholder')}}</view> <view class="text-[#888] text-[28rpx]" v-else>{{t('selectAddressPlaceholder')}}</view>
<view class="flex items-center"> <view class="flex items-center">
<text class="nc-iconfont nc-icon-dizhiguanliV6xx text-[32rpx] mr-[4rpx] text-[#e93323]"></text> <text class="nc-iconfont nc-icon-dizhiguanliV6xx text-[32rpx] mr-[4rpx] text-[var(--primary-color)]"></text>
<text class="text-[24rpx] whitespace-nowrap text-[#e93323]">定位</text> <text class="text-[24rpx] whitespace-nowrap text-[var(--primary-color)]">定位</text>
</view> </view>
</view> </view>
</u-form-item> </u-form-item>

View File

@ -7,6 +7,7 @@
<view class="flex pt-[30rpx] pb-[8rpx] items-center border-0 border-b-[2rpx] border-solid border-[#F1F2F5]"> <view class="flex pt-[30rpx] pb-[8rpx] items-center border-0 border-b-[2rpx] border-solid border-[#F1F2F5]">
<text class="pt-[4rpx] text-[44rpx] text-[#333] iconfont iconrenminbiV6xx price-font "></text> <text class="pt-[4rpx] text-[44rpx] text-[#333] iconfont iconrenminbiV6xx price-font "></text>
<input type="digit" class="h-[76rpx] leading-[76rpx] pl-[10rpx] flex-1 font-500 text-[54rpx] bg-[#fff]" v-model="applyData.apply_money" maxlength="7" :placeholder="applyData.apply_money?'':(t('minWithdrawal') + t('currency') + moneyFormat(config.min))" placeholder-class="apply-price" :adjust-position="false"/> <input type="digit" class="h-[76rpx] leading-[76rpx] pl-[10rpx] flex-1 font-500 text-[54rpx] bg-[#fff]" v-model="applyData.apply_money" maxlength="7" :placeholder="applyData.apply_money?'':(t('minWithdrawal') + t('currency') + moneyFormat(config.min))" placeholder-class="apply-price" :adjust-position="false"/>
<text v-if="Number(serviceMoney)" class="text-[24rpx] text-[var(--text-color-light6)] mr-[20rpx]">手续费{{ serviceMoney }}</text>
<text @click="clearMoney" v-if="applyData.apply_money" class="nc-iconfont nc-icon-cuohaoV6xx1 !text-[32rpx] text-[var(--text-color-light9)]"></text> <text @click="clearMoney" v-if="applyData.apply_money" class="nc-iconfont nc-icon-cuohaoV6xx1 !text-[32rpx] text-[var(--text-color-light9)]"></text>
</view> </view>
<view class="pt-[16rpx] flex items-center justify-between px-[4rpx]"> <view class="pt-[16rpx] flex items-center justify-between px-[4rpx]">
@ -21,7 +22,7 @@
<view class="mt-[20rpx] card-template"> <view class="mt-[20rpx] card-template">
<view class="font-500 text-[30rpx] text-[#333] leading-[42rpx] mb-[30rpx]">到账方式</view> <view class="font-500 text-[30rpx] text-[#333] leading-[42rpx] mb-[30rpx]">到账方式</view>
<!-- 提现到微信 --> <!-- 提现到微信 -->
<view class="p-[20rpx] mb-[20rpx] flex items-center rounded-[var(--rounded-mid)] border-[1rpx] border-solid border-[#eee]" v-if="config.transfer_type.includes('wechatpay') && memberStore.info && (memberStore.info.wx_openid || memberStore.info.weapp_openid)" @click="applyData.transfer_type = 'wechatpay'" :class="{'border-[#00C800] bg-[#ECF9EF]': applyData.transfer_type == 'wechatpay'}"> <view class="p-[20rpx] mb-[20rpx] flex items-center rounded-[var(--rounded-mid)] border-[1rpx] border-solid border-[#eee]" v-if="config.transfer_type.includes('wechatpay') && memberStore.info && (memberStore.info.wx_openid || memberStore.info.weapp_openid)" :class="{'border-[#00C800] bg-[#ECF9EF]': applyData.transfer_type == 'wechatpay'}" @click="transferWeixin">
<view> <view>
<image class="h-[60rpx] w-[60rpx]" :src="img('static/resource/images/member/apply_withdrawal/wechat.png')" mode="widthFix" /> <image class="h-[60rpx] w-[60rpx]" :src="img('static/resource/images/member/apply_withdrawal/wechat.png')" mode="widthFix" />
</view> </view>
@ -31,6 +32,27 @@
</view> </view>
</view> </view>
<!-- 提现到微信收款码 -->
<view class="p-[20rpx] mb-[20rpx] flex items-center rounded-[var(--rounded-mid)] border-[1rpx] border-solid border-[#eee]" v-if="config.transfer_type.includes('wechat_code')" :class="{'border-[#00C800] bg-[#ECF9EF]': applyData.transfer_type == 'wechat_code' && wechatCodeInfo}" >
<view @click="transferWechatCode" >
<image class="h-[60rpx] w-[60rpx] align-middle" :src="img('static/resource/images/member/apply_withdrawal/wechat_code.png')" mode="widthFix" />
</view>
<view class="flex-1 px-[22rpx]" @click="transferWechatCode" >
<view class="text-[28rpx] text-[#333] leading-[40rpx] mb-[6rpx]">{{ t('cashOutToWechatCode') }}</view>
<view class="text-[var(--text-color-light9)] text-[24rpx] leading-[34rpx]">
<view v-if="wechatCodeInfo" class="truncate max-w-[440rpx]">
<text>{{ t('cashOutTo') }}{{ t('wechatCodeAccountNo') }}</text>
<text class="text-[#333]">{{ wechatCodeInfo.account_no }}</text>
</view>
<view v-else>{{ t('cashOutToWechatCodeTips') }}</view>
</view>
</view>
<view class="flex items-center">
<button v-if="!wechatCodeInfo && !wechatCodeLoading" hover-class="none" class="w-[110rpx] h-[54rpx] flex-center rounded-full p-[0] text-[var(--primary-color)] bg-transparent border-[2rpx] border-solid border-[var(--primary-color)] text-[22rpx]" @click="redirect({ url: '/app/pages/member/account', param: { type: 'wechat_code', mode: 'select' } , mode: 'redirectTo'})">{{t('toAdd')}}</button>
<text v-else class="nc-iconfont nc-icon-youV6xx text-[28rpx] text-[var(--text-color-light9)] p-[10rpx]" @click.stop="redirect({ url: '/app/pages/member/account', param: { type: 'wechat_code', mode: 'select' } , mode: 'redirectTo'})"></text>
</view>
</view>
<!-- 提现到支付宝 --> <!-- 提现到支付宝 -->
<view class="p-[20rpx] mb-[20rpx] flex items-center rounded-[var(--rounded-mid)] border-[1rpx] border-solid border-[#eee]" v-if="config.transfer_type.includes('alipay')" :class="{'border-[#009FE8] bg-[#EEF8FC]': applyData.transfer_type == 'alipay' && alipayAccountInfo}" > <view class="p-[20rpx] mb-[20rpx] flex items-center rounded-[var(--rounded-mid)] border-[1rpx] border-solid border-[#eee]" v-if="config.transfer_type.includes('alipay')" :class="{'border-[#009FE8] bg-[#EEF8FC]': applyData.transfer_type == 'alipay' && alipayAccountInfo}" >
<view @click="transferAlipay" > <view @click="transferAlipay" >
@ -77,7 +99,7 @@
</view> </view>
<view class="tab-bar-placeholder"></view> <view class="tab-bar-placeholder"></view>
<view class="fixed bottom-[0] tab-bar left-0 right-0 px-[var(--sidebar-m)]"> <view class="fixed bottom-[0] tab-bar left-0 right-0 px-[var(--sidebar-m)] bg-[var(--page-bg-color)]">
<button class="h-[80rpx] !text-[#fff] leading-[80rpx] primary-btn-bg rounded-[50rpx] text-[26rpx]" :disabled="applyData.apply_money == '' || applyData.apply_money == 0" :loading="loading" @click="cashOut">{{t('cashOutNow')}}</button> <button class="h-[80rpx] !text-[#fff] leading-[80rpx] primary-btn-bg rounded-[50rpx] text-[26rpx]" :disabled="applyData.apply_money == '' || applyData.apply_money == 0" :loading="loading" @click="cashOut">{{t('cashOutNow')}}</button>
<view class="mt-[30rpx] text-center text-[26rpx] text-primary" @click.stop="redirect({ url: '/app/pages/member/cash_out'})"> <view class="mt-[30rpx] text-center text-[26rpx] text-primary" @click.stop="redirect({ url: '/app/pages/member/cash_out'})">
{{t('cashOutList')}} {{t('cashOutList')}}
@ -99,7 +121,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, watch, computed } from 'vue' import { ref, reactive, watch, computed } from 'vue'
import { t } from '@/locale' import { t } from '@/locale'
import { moneyFormat, redirect, getToken ,img, deepClone } from '@/utils/common' import { moneyFormat, redirect, getToken ,img, deepClone, getWinxinOpenId } from '@/utils/common'
import useMemberStore from '@/stores/member' import useMemberStore from '@/stores/member'
import { cashOutConfig, cashOutApply, getFirstCashoutAccountInfo, getCashoutAccountInfo } from '@/app/api/member' import { cashOutConfig, cashOutApply, getFirstCashoutAccountInfo, getCashoutAccountInfo } from '@/app/api/member'
import { onLoad } from '@dcloudio/uni-app' import { onLoad } from '@dcloudio/uni-app'
@ -113,7 +135,11 @@
apply_money: '', apply_money: '',
transfer_type: '', transfer_type: '',
account_type: 'money', account_type: 'money',
account_id: 0 account_id: 0,
transfer_payee:{
open_id: '',
channel: ''
}
}) })
// //
@ -129,6 +155,9 @@
case 'alipay': case 'alipay':
applyData.account_id = alipayAccountInfo.value ? alipayAccountInfo.value.account_id : 0 applyData.account_id = alipayAccountInfo.value ? alipayAccountInfo.value.account_id : 0
break; break;
case 'wechat_code':
applyData.account_id = wechatCodeInfo.value ? wechatCodeInfo.value.account_id : 0
break;
default: default:
applyData.account_id = 0 applyData.account_id = 0
} }
@ -181,9 +210,14 @@
for (let key in deepClone(res.data)) { for (let key in deepClone(res.data)) {
config[key] = deepClone(res.data[key]); config[key] = deepClone(res.data[key]);
} }
if (config.transfer_type.includes('wechatpay') && memberStore.info && (!memberStore.info.wx_openid && !memberStore.info.weapp_openid)) config.transfer_type.splice(config.transfer_type.indexOf('wechatpay'),1) if (config.transfer_type.includes('wechatpay') && memberStore.info && (!memberStore.info.wx_openid && !memberStore.info.weapp_openid)){
config.transfer_type.splice(config.transfer_type.indexOf('wechatpay'),1)
} else{
config.transfer_type.includes('wechatpay') && transferWeixin()
}
config.transfer_type.includes('bank') && getBankAccountInfo() config.transfer_type.includes('bank') && getBankAccountInfo()
config.transfer_type.includes('alipay') && getAlipayAccountInfo() config.transfer_type.includes('alipay') && getAlipayAccountInfo()
config.transfer_type.includes('wechat_code') && getWechatCodeInfo()
applyData.transfer_type = config.transfer_type[0] applyData.transfer_type = config.transfer_type[0]
if(query.type){ if(query.type){
applyData.transfer_type = query.type applyData.transfer_type = query.type
@ -192,6 +226,15 @@
}) })
}) })
//
const serviceMoney = computed(() => {
let money = 0
if(applyData.apply_money && Number(config.rate)){
money = Number(applyData.apply_money) * Number(config.rate) / 100
}
return money.toFixed(2);
})
// //
const allMoney = () => { const allMoney = () => {
if(parseFloat(cashOutMoney.value)) applyData.apply_money = moneyFormat(cashOutMoney.value) if(parseFloat(cashOutMoney.value)) applyData.apply_money = moneyFormat(cashOutMoney.value)
@ -278,6 +321,33 @@
}) })
} }
/**
* 获取微信收款码提现账号信息
*/
const wechatCodeLoading = ref(false)
const wechatCodeInfo: any = ref(null)
const getWechatCodeInfo = () => {
const data = { account_type: 'wechat_code', account_id: 0 }
let request = getFirstCashoutAccountInfo
if (query.type && query.type == 'wechat_code' && query.account_id) {
request = getCashoutAccountInfo
data.account_id = query.account_id
}
wechatCodeLoading.value = true
request(data).then((res: any) => {
if (res.data && res.data.account_id) {
wechatCodeInfo.value = res.data
//
if(applyData.transfer_type == 'wechat_code' && !applyData.account_id){
applyData.account_id = wechatCodeInfo.value.account_id;
}
}
wechatCodeLoading.value = false
})
}
/** /**
* 申请提现 * 申请提现
*/ */
@ -286,9 +356,15 @@
if (loading.value) return if (loading.value) return
loading.value = true loading.value = true
cashOutApply(applyData).then(res => { cashOutApply(applyData).then((res: any) => {
loading.value = false loading.value = false
memberStore.getMemberInfo(()=>{redirect({ url: '/app/pages/member/cash_out' })}) memberStore.getMemberInfo(()=>{
if(applyData.transfer_type == 'wechatpay'){
redirect({ url: '/app/pages/member/cash_out_detail', param: { id: res.data} })
}else{
redirect({ url: '/app/pages/member/cash_out' })
}
})
}).catch(() => { }).catch(() => {
loading.value = false loading.value = false
@ -312,6 +388,24 @@
} }
applyData.transfer_type = 'bank' applyData.transfer_type = 'bank'
} }
//
const transferWeixin = ()=>{
let data = getWinxinOpenId();
applyData.transfer_payee.open_id = data.wechat ? data.wechat : data.weapp;
applyData.transfer_payee.channel = data.wechat ? 'wechat' : 'weapp';
applyData.transfer_type = 'wechatpay'
}
//
const transferWechatCode = ()=>{
if(!wechatCodeInfo.value){
uni.showToast({ title: t('cashOutToWechatCodeTips'), icon: 'none' })
return false
}
applyData.transfer_type = 'wechat_code'
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -93,6 +93,7 @@
const cashOutConfigObj: any = reactive({}) const cashOutConfigObj: any = reactive({})
const rechargeConfigObj: any = reactive({}) const rechargeConfigObj: any = reactive({})
// siteAddons // siteAddons
watch( watch(
() => systemStore.siteAddons, () => systemStore.siteAddons,
@ -109,6 +110,7 @@
} }
} }
); );
onShow(() => { onShow(() => {
cashOutConfig().then((res: any) => { cashOutConfig().then((res: any) => {
for (let key in res.data) { for (let key in res.data) {

View File

@ -2,17 +2,21 @@
<view class="min-h-[100vh] bg-[var(--page-bg-color)] overflow-hidden" :style="themeColor()"> <view class="min-h-[100vh] bg-[var(--page-bg-color)] overflow-hidden" :style="themeColor()">
<block v-if="!loading"> <block v-if="!loading">
<view class="sidebar-margin card-template mt-[20rpx] !pt-[60rpx] !pb-[40rpx]"> <view class="sidebar-margin card-template mt-[20rpx] !pt-[60rpx] !pb-[40rpx]">
<view class="flex items-center flex-col mb-[80rpx]"> <view class="flex flex-col justify-center items-center mb-[44rpx]" v-if="cashOutInfo.status == 4 && cashOutInfo.transfer_type == 'wechatpay'">
<text class="text-[60rpx] font-bold price-font mb-[20rpx]">-{{ cashOutInfo.apply_money }}</text> <image class="h-[70rpx] w-[70rpx] mb-[24rpx]" :src="img('static/resource/images/member/apply_withdrawal/transfer.png')" mode="widthFix" />
<text class="text-[28rpx] text-[#333]" :class="{'text-primary': cashOutInfo.status == 1}">{{ cashOutInfo.status_name }}</text> <view class="text-[28rpx] text-[#333]">处理中预计2小时内到账</view>
</view> </view>
<!-- 状态1.待审核2.待转账 3.已转账 -1拒绝' --> <view class="flex items-center flex-col mb-[80rpx]">
<text class="text-[60rpx] font-bold price-font mb-[16rpx]">{{ cashOutInfo.apply_money }}</text>
<text class="text-[28rpx] text-[#333]" :class="{'text-primary': cashOutInfo.status == 1, 'text-[#999]': cashOutInfo.status == 4 }">{{ cashOutInfo.status_name }}</text>
</view>
<!-- 状态1.待审核2.待转账 3.已转账 4.转账中 -1拒绝' -->
<view> <view>
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]"> <view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]">
<text class="text-[#333] w-[200rpx]">{{t('cashOutNo')}}</text> <text class="text-[#333] w-[200rpx]">{{t('cashOutNo')}}</text>
<text class="text-[#333]">{{ cashOutInfo.cash_out_no }}</text> <text class="text-[#333]">{{ cashOutInfo.cash_out_no }}</text>
</view> </view>
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]"> <view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]" v-if="Number(cashOutInfo.service_money)">
<text class="text-[#333] w-[200rpx]">{{t('serviceMoney')}}</text> <text class="text-[#333] w-[200rpx]">{{t('serviceMoney')}}</text>
<text class="text-[#333]">{{ cashOutInfo.service_money }}</text> <text class="text-[#333]">{{ cashOutInfo.service_money }}</text>
</view> </view>
@ -24,26 +28,75 @@
<text class="text-[#333] w-[200rpx]">{{t('auditTime')}}</text> <text class="text-[#333] w-[200rpx]">{{t('auditTime')}}</text>
<text class="text-[#333]">{{ cashOutInfo.audit_time }}</text> <text class="text-[#333]">{{ cashOutInfo.audit_time }}</text>
</view> </view>
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]" v-if="cashOutInfo.transfer_bank">
<text class="text-[#333] w-[200rpx]">{{t('transferBank')}}</text>
<text class="text-[#333] truncate">{{ cashOutInfo.transfer_bank }}</text>
</view>
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]">
<text class="text-[#333] w-[200rpx]">{{t('transferAccount')}}</text>
<text class="text-[#333] truncate">{{ cashOutInfo.transfer_type == 'wechatpay' ? '微信' : cashOutInfo.transfer_account }}</text>
</view>
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]" v-if="cashOutInfo.status == -1 && cashOutInfo.refuse_reason"> <view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]" v-if="cashOutInfo.status == -1 && cashOutInfo.refuse_reason">
<text class="text-[#333] w-[200rpx]">{{t('refuseReason')}}</text> <text class="text-[#333] w-[200rpx] flex-shrink-0">{{t('refuseReason')}}</text>
<text class="text-[#333] truncate">{{ cashOutInfo.refuse_reason }}</text> <text class="text-[#333] ml-[20rpx]">{{ cashOutInfo.refuse_reason }}</text>
</view> </view>
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]" v-if="cashOutInfo.status == 3"> <view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]" v-if="cashOutInfo.status == 3">
<text class="text-[#333] w-[200rpx]">{{t('transferTypeName')}}</text> <text class="text-[#333] w-[200rpx]">{{t('transferTypeName')}}</text>
<text class="text-[#333] truncate">{{ cashOutInfo.transfer_type_name }}</text> <text class="text-[#333] truncate">{{ cashOutInfo.transfer_type_name }}</text>
</view> </view>
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]" v-if="cashOutInfo.status == 3 && cashOutInfo.transfer_time"> </view>
</view>
<view class="sidebar-margin card-template mt-[20rpx]" v-if="cashOutInfo.transfer_type == 'wechatpay' && (cashOutInfo.status == 2 || cashOutInfo.status == 3 || cashOutInfo.status == 4)">
<u-steps :current="current" direction="column" activeColor="var(--primary-color)" activeIcon="checkmark-circle-fill" inactiveIcon="checkmark-circle-fill">
<u-steps-item title="提现申请" :desc="cashOutInfo.create_time" ></u-steps-item>
<u-steps-item title="处理中" :desc="cashOutInfo.audit_time ? cashOutInfo.audit_time : '-'"></u-steps-item>
<u-steps-item title="提现成功" :desc="cashOutInfo.transfer_time ? cashOutInfo.transfer_time : '-' "></u-steps-item>
</u-steps>
</view>
<view class="sidebar-margin card-template mt-[20rpx]">
<view class="title">{{ t('proceedsInfo') }}</view>
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]">
<text class="text-[#333] w-[200rpx]">{{t('transferAccount')}}</text>
<text class="text-[#333] truncate">{{ cashOutInfo.transfer_type == 'wechatpay' ? '微信零钱' : cashOutInfo.transfer_account }}</text>
</view>
<template v-if="cashOutInfo.transfer_type == 'wechatpay'">
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]">
<text class="text-[#333] w-[200rpx]">{{ t('transferNickname') }}</text>
<text class="text-[#333]">{{ cashOutInfo.nickname }}</text>
</view>
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]">
<text class="text-[#333] w-[200rpx]">{{ t('transferImg') }}</text>
<u-avatar :src="img(cashOutInfo.headimg)" size="30" leftIcon="none" :default-url="img('static/resource/images/default_headimg.png')" />
</view>
</template>
<template v-if="cashOutInfo.transfer_type !== 'wechatpay'">
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]">
<text class="text-[#333] w-[200rpx]">{{cashOutInfo.transfer_type == 'bank' ? t('bankRealname') : t('realname')}}</text>
<text class="text-[#333]">{{ cashOutInfo.transfer_realname }}</text>
</view>
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]" v-if="cashOutInfo.transfer_bank">
<text class="text-[#333] w-[200rpx]">{{t('transferBank')}}</text>
<text class="text-[#333] truncate">{{ cashOutInfo.transfer_bank }}</text>
</view>
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]" v-if="cashOutInfo.transfer_type == 'wechat_code' || cashOutInfo.transfer_type == 'alipay'">
<text class="text-[#333] w-[200rpx]">{{t('transferCode')}}</text>
<view class="flex items-center" @click="previewImg()">
<image :src="img(cashOutInfo.transfer_payment_code)" class="w-[140rpx] h-[140rpx] rounded-[var(--rounded-small)]"></image>
</view>
</view>
</template>
</view>
<view class="sidebar-margin card-template my-[20rpx]" v-if="cashOutInfo.status == 3">
<view class="title">{{ t('transferInfo') }}</view>
<view class="flex justify-between text-[28rpx] leading-[32rpx]" v-if="cashOutInfo.transfer_time">
<text class="text-[#333] w-[200rpx]">{{t('transferTime')}}</text> <text class="text-[#333] w-[200rpx]">{{t('transferTime')}}</text>
<text class="text-[#333] truncate">{{ cashOutInfo.transfer_time }}</text> <text class="text-[#333] truncate">{{ cashOutInfo.transfer_time }}</text>
</view> </view>
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]" v-if="cashOutInfo.transfer_no">
<text class="text-[#333] w-[200rpx]">{{ t('transferNo') }}</text>
<text class="text-[#333] truncate">{{ cashOutInfo.transfer.transfer_no }}</text>
</view>
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]" v-if="cashOutInfo.transfer && cashOutInfo.transfer.transfer_remark">
<text class="text-[#333] w-[200rpx] flex-shrink-0">{{t('transferRemark')}}</text>
<text class="text-[#333] ml-[20rpx]">{{ cashOutInfo.transfer.transfer_remark }}</text>
</view>
<view class="flex justify-between text-[28rpx] mt-[34rpx] leading-[32rpx]" v-if="cashOutInfo.transfer && cashOutInfo.transfer.transfer_voucher">
<text class="text-[#333] w-[200rpx]">{{t('transferVoucher')}}</text>
<view class="flex items-center" @click="handleImg()">
<image :src="img(cashOutInfo.transfer.transfer_voucher)" class="w-[140rpx] h-[140rpx] rounded-[var(--rounded-small)]"></image>
</view>
</view> </view>
</view> </view>
</block> </block>
@ -53,12 +106,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app' import { onLoad, onUnload } from '@dcloudio/uni-app'
import { t } from '@/locale' import { t } from '@/locale'
import { redirect, img, goback } from '@/utils/common'; import { redirect, img, goback } from '@/utils/common';
import { getCashOutDetail } from '@/app/api/member'; import { getCashOutDetail } from '@/app/api/member';
const cashOutInfo = ref({}); const cashOutInfo = ref<any>({});
const current = ref(0);
const loading = ref<boolean>(true); const loading = ref<boolean>(true);
onLoad((option: any) => { onLoad((option: any) => {
let id = option.id || ""; let id = option.id || "";
@ -79,12 +133,56 @@ const getCashoutAccountListFn = (id: any) => {
getCashOutDetail(id).then((res: any) => { getCashOutDetail(id).then((res: any) => {
cashOutInfo.value = res.data; cashOutInfo.value = res.data;
if(cashOutInfo.value.transfer_type == 'wechatpay'){
if(cashOutInfo.value.status == 1){
current.value = 0;
}else if(cashOutInfo.value.status == 2 || cashOutInfo.value.status == 4){
current.value = 1;
}else if(cashOutInfo.value.status == 3){
current.value = 2;
}
}
loading.value = false; loading.value = false;
}).catch(() => { }).catch(() => {
loading.value = false; loading.value = false;
}) })
} }
const previewImg = () =>{
uni.previewImage({
urls: [img(cashOutInfo.value.transfer_payment_code)],
indicator: "number",
loop: true
})
}
const handleImg = () => {
uni.previewImage({
urls: [img(cashOutInfo.value.transfer.transfer_voucher)],
indicator: "number",
loop: true
})
}
//
onUnload(()=>{
// #ifdef H5 || APP
try {
uni.closePreviewImage()
}catch (e) {
}
// #endif
})
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
:deep(.u-text__value--main){
font-size: 24rpx !important;
color: #606266;
}
:deep(.u-text__value){
font-size: 24rpx !important;
color: #606266;
}
</style> </style>

View File

@ -5,16 +5,9 @@
<view v-show="!diy.getLoading()"> <view v-show="!diy.getLoading()">
<!-- 自定义模板渲染 --> <!-- 自定义模板渲染 -->
<view class="diy-template-wrap bg-index" v-if="diy.data.pageMode != 'fixed'" :style="diy.pageStyle()"> <view class="diy-template-wrap bg-index" :style="diy.pageStyle()">
<diy-group ref="diyGroupRef" :data="diy.data" :pullDownRefreshCount="diy.pullDownRefreshCount" /> <diy-group ref="diyGroupRef" :data="diy.data" />
</view>
<!-- 固定模板渲染 -->
<view class="fixed-template-wrap" v-if="diy.data.pageMode == 'fixed'">
<fixed-group :data="diy.data" :pullDownRefreshCount="diy.pullDownRefreshCount" />
</view> </view>
@ -33,11 +26,8 @@
import {useDiy} from '@/hooks/useDiy' import {useDiy} from '@/hooks/useDiy'
import {redirect} from '@/utils/common'; import {redirect} from '@/utils/common';
import diyGroup from '@/addon/components/diy/group/index.vue' import diyGroup from '@/addon/components/diy/group/index.vue'
import fixedGroup from '@/addon/components/fixed/group/index.vue'
import useMemberStore from '@/stores/member' import useMemberStore from '@/stores/member'
uni.hideTabBar() // tabbar
// //
const memberStore = useMemberStore() const memberStore = useMemberStore()
const userInfo = computed(() => memberStore.info) const userInfo = computed(() => memberStore.info)
@ -80,9 +70,6 @@
// //
diy.onUnload(); diy.onUnload();
//
diy.onPullDownRefresh()
// //
diy.onPageScroll() diy.onPageScroll()
</script> </script>

View File

@ -11,8 +11,8 @@
<view class="type-class"> <view class="type-class">
<u-popup :show="typePopup" mode="top" @close="typePopup = false"> <u-popup :show="typePopup" mode="top" @close="typePopup = false">
<view @touchmove.prevent.stop class="py-[22rpx]"> <view @touchmove.prevent.stop class="py-[22rpx]">
<view class="leading-[80rpx] text-[26rpx] text-[#333] px-[50rpx]" :class="{'bg-[#FDF8F8] !text-primary font-500' : from_type == ''}" @click="searchTypeFn()">全部</view> <view class="leading-[80rpx] text-[26rpx] text-[#333] px-[50rpx]" :class="{'bg-[var(--primary-color-light)] !text-primary font-500' : from_type == ''}" @click="searchTypeFn()">全部</view>
<view class="leading-[80rpx] text-[26rpx] text-[#333] px-[50rpx]" :class="{'bg-[#FDF8F8] !text-primary font-500' : from_type == index}" v-for="(item,index) in pointType" @click="searchTypeFn(index,item)">{{ item.name }}</view> <view class="leading-[80rpx] text-[26rpx] text-[#333] px-[50rpx]" :class="{'bg-[var(--primary-color-light)] !text-primary font-500' : from_type == index}" v-for="(item,index) in pointType" @click="searchTypeFn(index,item)">{{ item.name }}</view>
</view> </view>
</u-popup> </u-popup>
</view> </view>

View File

@ -63,7 +63,7 @@
</template> </template>
</view> </view>
</view> </view>
<view class="w-[10rpx] h-[10rpx] rounded-[50%] bg-[#FF5527]" v-if="isCurrentDate(item)"></view> <view class="w-[10rpx] h-[10rpx] rounded-[50%] bg-[var(--primary-color)]" v-if="isCurrentDate(item)"></view>
</view> </view>
</block> </block>
</view> </view>
@ -81,7 +81,7 @@
</template> </template>
</view> </view>
</view> </view>
<view class="w-[10rpx] h-[10rpx] rounded-[50%] bg-[#FF5527]" v-if="isCurrentDate(item)"></view> <view class="w-[10rpx] h-[10rpx] rounded-[50%] bg-[var(--primary-color)]" v-if="isCurrentDate(item)"></view>
</view> </view>
</block> </block>
</view> </view>

View File

@ -5,6 +5,7 @@ import { useLogin } from '@/hooks/useLogin';
interface Diy { interface Diy {
mode: string, // 模式decorate 装修,为空表示正常 mode: string, // 模式decorate 装修,为空表示正常
id: any,
pageMode: string, // 页面展示模式diy自定义fixed固定 pageMode: string, // 页面展示模式diy自定义fixed固定
currentIndex: number, currentIndex: number,
global: { global: {
@ -18,13 +19,15 @@ interface Diy {
value: any[], value: any[],
topFixedStatus: string, // 置顶组件的状态 topFixedStatus: string, // 置顶组件的状态
scrollTop: number, scrollTop: number,
topTabarHeight: number topTabarHeight: number,
componentRefs: any
} }
const useDiyStore = defineStore('diy', { const useDiyStore = defineStore('diy', {
state: (): Diy => { state: (): Diy => {
return { return {
mode: '', mode: '',
id: 0,
pageMode: 'diy', pageMode: 'diy',
currentIndex: -99, currentIndex: -99,
global: { global: {
@ -37,7 +40,8 @@ const useDiyStore = defineStore('diy', {
value: [], // 组件集合 value: [], // 组件集合
topFixedStatus: 'home', // 顶部 置顶组件状态home展示首页数据、diy展示置顶组件定义的子页面 topFixedStatus: 'home', // 顶部 置顶组件状态home展示首页数据、diy展示置顶组件定义的子页面
scrollTop: 0, // 滚动位置 scrollTop: 0, // 滚动位置
topTabarHeight: 0 topTabarHeight: 0,
componentRefs: null
} }
}, },
getters: {}, getters: {},
@ -131,7 +135,7 @@ const useDiyStore = defineStore('diy', {
} }
diyRedirect(data); diyRedirect(data);
} }
} },
} }
}) })