Compare commits
47 Commits
5f60504c75
...
temp2797
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b81ecdf98 | |||
| c536fac4d2 | |||
| 475449f04e | |||
| e40b46a888 | |||
| a81386d336 | |||
| b69eeba74b | |||
| 82139e616d | |||
| ed6d9f563d | |||
| 3a8ad7d871 | |||
| ec960a29c4 | |||
| 8312de5d09 | |||
| 5833a244de | |||
| 92fe95d131 | |||
| 07718235c3 | |||
| 183181a15d | |||
| a4f2c81528 | |||
| 30b66ad7b3 | |||
| 077fac40c2 | |||
| 10d45b1c3b | |||
| 60dc054383 | |||
| ef8c7568ac | |||
| 551d6c103d | |||
| 4a92d9bec2 | |||
| ff4340c3a9 | |||
| 4303a93278 | |||
| 12e4493a17 | |||
| c94e214d05 | |||
| afede79047 | |||
| ab727eef1d | |||
| ee050f5df9 | |||
| 855fbb2fe5 | |||
| b706e67e82 | |||
| 557addb76b | |||
| dacc25b281 | |||
| ec4fb3e688 | |||
| 75e40c752b | |||
| 24df6f0dd5 | |||
| a8ab093eae | |||
| 7f4b1d6d13 | |||
| 1420b7435e | |||
| 8b331e53d3 | |||
| 10fd785b4c | |||
| 2c0b8ed076 | |||
| ab8625ebaf | |||
| bbef0ecf55 | |||
| 671c350d95 | |||
| 2d19a653c9 |
15
.gitignore
vendored
15
.gitignore
vendored
@@ -61,6 +61,21 @@ stub-app-projects/mp-harmony/hvigor
|
|||||||
# Trae/IDE 临时文件
|
# Trae/IDE 临时文件
|
||||||
.hvigor/outputs
|
.hvigor/outputs
|
||||||
.trae_permission_check_*
|
.trae_permission_check_*
|
||||||
|
|
||||||
|
# 本地测试脚本与产物归档(不入 git, 留本地复用)
|
||||||
|
# 内容: Playwright 截图 / 测试脚本 / 对比图 / 临时提交草稿
|
||||||
|
# 复用方式: cd _test_archive && python xxx.py
|
||||||
|
_test_archive/
|
||||||
|
|
||||||
|
# 兜底:防止任何临时测试文件被遗漏在根目录
|
||||||
|
# 名称以 _test_/_compare_/_auth_ 开头, 或 check_button.py 等
|
||||||
|
_test_*.png
|
||||||
|
_test_*.html
|
||||||
|
_compare*.png
|
||||||
|
_compare*.py
|
||||||
|
_auth_*.json
|
||||||
|
_commit_msg.txt
|
||||||
|
/check_button.py
|
||||||
# 鸿蒙签名材料 (敏感, 绝不能提交到公共 git)
|
# 鸿蒙签名材料 (敏感, 绝不能提交到公共 git)
|
||||||
# 仓库有 origin (github.com/Zajd/jsysq-uniapp) 是公开仓库, 签名材料必须 gitignore
|
# 仓库有 origin (github.com/Zajd/jsysq-uniapp) 是公开仓库, 签名材料必须 gitignore
|
||||||
stub-app-projects/mp-harmony/**/cert-profile
|
stub-app-projects/mp-harmony/**/cert-profile
|
||||||
|
|||||||
@@ -155,7 +155,15 @@ const localDevConfig = ({
|
|||||||
'248': { // 大宗商品贸易,支持自定义字体大小、颜色
|
'248': { // 大宗商品贸易,支持自定义字体大小、颜色
|
||||||
uniacid: 248,
|
uniacid: 248,
|
||||||
domain: 'https://5g.aigc-quickapp.com/',
|
domain: 'https://5g.aigc-quickapp.com/',
|
||||||
},
|
},
|
||||||
|
'228': { // 商品分类,没有立即咨询文字
|
||||||
|
uniacid: 228,
|
||||||
|
domain: 'https://5g.aigc-quickapp.com/',
|
||||||
|
},
|
||||||
|
'2797': { //
|
||||||
|
uniacid: 2797,
|
||||||
|
domain: 'https://xcx.aigc-quickapp.com/',
|
||||||
|
},
|
||||||
// })['5g-237']; // 商品分类,没有立即咨询文字
|
// })['5g-237']; // 商品分类,没有立即咨询文字
|
||||||
// })['5g-97']; // 大宗商品贸易,支持自定义字体大小、颜色
|
// })['5g-97']; // 大宗商品贸易,支持自定义字体大小、颜色
|
||||||
// })['5g-50']; // 燃气安全产品机器人,购物车无反应
|
// })['5g-50']; // 燃气安全产品机器人,购物车无反应
|
||||||
@@ -169,7 +177,7 @@ const localDevConfig = ({
|
|||||||
// })['2724']; // 生物菌肥
|
// })['2724']; // 生物菌肥
|
||||||
// })['2811']; // 选择要使用的环境配置
|
// })['2811']; // 选择要使用的环境配置
|
||||||
// })['local-11']; // 选择要使用的环境配置
|
// })['local-11']; // 选择要使用的环境配置
|
||||||
})['192.168.8.118-1']; // 本地
|
})['2797']; // 本地
|
||||||
// })['local-2']; // 本地
|
// })['local-2']; // 本地
|
||||||
// })['local-11']; // 本地
|
// })['local-11']; // 本地
|
||||||
// })['1-test']; // test
|
// })['1-test']; // test
|
||||||
|
|||||||
@@ -102,6 +102,36 @@ export function parsePageParams(raw, defaultValue = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把任意值(通常是 URL 参数 / query string 解析出的字符串)强转为布尔值。
|
||||||
|
*
|
||||||
|
* 设计动机:URL query 中布尔值的合法写法不止一种——
|
||||||
|
* ?show_home=1 -> '1' -> true
|
||||||
|
* ?show_home=true -> 'true' -> true
|
||||||
|
* ?show_home=true -> true -> true
|
||||||
|
* ?show_home=0 -> '0' -> false
|
||||||
|
* ?show_home=false -> 'false' -> false
|
||||||
|
* ?show_home= -> '' -> false
|
||||||
|
* 未传 -> undefined -> false
|
||||||
|
*
|
||||||
|
* 与裸 `Boolean(x)` 的差异:
|
||||||
|
* Boolean('false') === true (任何非空字符串都是 true)
|
||||||
|
* Boolean('0') === true (同上)
|
||||||
|
* 本函数对这两个值返回 false,更适合 URL 参数解析场景。
|
||||||
|
*
|
||||||
|
* @param {*} val 任意值
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export function coerceBool(val) {
|
||||||
|
if (val === true || val === 1) return true;
|
||||||
|
if (typeof val === 'string') {
|
||||||
|
const s = val.trim().toLowerCase();
|
||||||
|
return s === '1' || s === 'true';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 关于页面路径,使用常量导出
|
// 关于页面路径,使用常量导出
|
||||||
export const INDEX_PAGE_URL = '/pages/index/index';
|
export const INDEX_PAGE_URL = '/pages/index/index';
|
||||||
export const CATEGORY_PAGE_URL = '/pages_goods/category';
|
export const CATEGORY_PAGE_URL = '/pages_goods/category';
|
||||||
@@ -457,6 +487,9 @@ export default {
|
|||||||
/** 把 page_params 还原成对象(JSON.parse 的安全封装) */
|
/** 把 page_params 还原成对象(JSON.parse 的安全封装) */
|
||||||
parsePageParams,
|
parsePageParams,
|
||||||
|
|
||||||
|
/** 把任意值强转为布尔值,专治 URL query 参数布尔解析(?show_home=1/'true'/true) */
|
||||||
|
coerceBool,
|
||||||
|
|
||||||
/** HTML 实体解码 */
|
/** HTML 实体解码 */
|
||||||
decodeEntity,
|
decodeEntity,
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,9 @@
|
|||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="footer-wrap footer-wrap-template1">
|
||||||
|
<view class="detail-btn" @tap.stop="toDetail(item)">{{ $lang('components-diy.diy-category-item.consultNow') }}</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -110,6 +113,9 @@
|
|||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="footer-wrap footer-wrap-template1">
|
||||||
|
<view class="detail-btn" @tap.stop="toDetail(item)">{{ $lang('components-diy.diy-category-item.consultNow') }}</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -173,6 +179,9 @@
|
|||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="footer-wrap footer-wrap-template1">
|
||||||
|
<view class="detail-btn" @tap.stop="toDetail(item)">{{ $lang('components-diy.diy-category-item.consultNow') }}</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -1420,6 +1429,26 @@ export default {
|
|||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
|
|
||||||
|
.footer-wrap-template1 {
|
||||||
|
padding: 16rpx 4rpx 0rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.detail-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 32rpx;
|
||||||
|
height: 55rpx;
|
||||||
|
background-color: #ff4545;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 25rpx;
|
||||||
|
border-radius: 26rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,6 +209,10 @@ export default {
|
|||||||
// 占位符:遗漏会被 ns-newform SCSS 默认值回退覆盖
|
// 占位符:遗漏会被 ns-newform SCSS 默认值回退覆盖
|
||||||
placeholderColor: pick('placeholderColor', 'placeholder_color'),
|
placeholderColor: pick('placeholderColor', 'placeholder_color'),
|
||||||
placeholderFontSize: pick('placeholderFontSize', 'placeholder_font_size'),
|
placeholderFontSize: pick('placeholderFontSize', 'placeholder_font_size'),
|
||||||
|
// textarea 显示行数 / 行高(透传给 ns-newform,与 ns-form 保持一致)
|
||||||
|
// 不传时 ns-newform 走自己的默认 4 行 / 44rpx
|
||||||
|
textareaRows: pick('textareaRows', 'textarea_rows'),
|
||||||
|
textareaLineHeight: pick('textareaLineHeight', 'textarea_line_height'),
|
||||||
// 输入框视觉增强(与 ns-newform 对齐)
|
// 输入框视觉增强(与 ns-newform 对齐)
|
||||||
inputBorderColor: pick('inputBorderColor', 'input_border_color'),
|
inputBorderColor: pick('inputBorderColor', 'input_border_color'),
|
||||||
inputBgColor: pick('inputBgColor', 'input_bg_color'),
|
inputBgColor: pick('inputBgColor', 'input_bg_color'),
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -54,17 +54,21 @@
|
|||||||
<!-- 视频播放弹窗 -->
|
<!-- 视频播放弹窗 -->
|
||||||
<uni-popup ref="videoPopup" type="center" style="background:transparent;width:100%;height:100%;">
|
<uni-popup ref="videoPopup" type="center" style="background:transparent;width:100%;height:100%;">
|
||||||
<view class="video-container" :style="popupVideoStyle">
|
<view class="video-container" :style="popupVideoStyle">
|
||||||
<video
|
<video
|
||||||
|
:key="video_url"
|
||||||
ref="popupVideoRef"
|
ref="popupVideoRef"
|
||||||
class="adaptive-video"
|
class="adaptive-video"
|
||||||
:autoPauseIfNavigate="true"
|
:autoPauseIfNavigate="true"
|
||||||
:autoPauseIfOpenNative="true"
|
:autoPauseIfOpenNative="true"
|
||||||
:autoplay="false"
|
:autoplay="false"
|
||||||
:enableAutoRotation="true"
|
:enableAutoRotation="true"
|
||||||
id="myVideo"
|
id="myVideo"
|
||||||
:src="video_url"
|
:src="video_url"
|
||||||
:controls="true"
|
:controls="true"
|
||||||
object-fit="contain"
|
object-fit="contain"
|
||||||
|
playsinline
|
||||||
|
webkit-playsinline
|
||||||
|
x5-playsinline
|
||||||
@loadedmetadata="onPopupVideoLoaded"
|
@loadedmetadata="onPopupVideoLoaded"
|
||||||
@error="onPopupVideoError"
|
@error="onPopupVideoError"
|
||||||
></video>
|
></video>
|
||||||
@@ -144,41 +148,21 @@ export default {
|
|||||||
|
|
||||||
// 弹窗视频容器样式
|
// 弹窗视频容器样式
|
||||||
popupVideoStyle() {
|
popupVideoStyle() {
|
||||||
let style = 'position:fixed;width:100%;left:0;';
|
// 容器横向铺满,黑色背景填满左右边界
|
||||||
const defaultRatio = 9 / 16;
|
// 视频用 object-fit:contain 保持原比例居中,左右黑边由容器黑色背景填充
|
||||||
|
let style = 'position:fixed;width:100%;height:50vh;left:50%;top:50%;transform:translate(-50%,-50%);border-radius:0;';
|
||||||
let ratio = defaultRatio;
|
|
||||||
if (this.popupVideoWidth && this.popupVideoHeight) {
|
|
||||||
ratio = this.popupVideoHeight / this.popupVideoWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isPortrait = ratio > 1;
|
|
||||||
|
|
||||||
if (isPortrait) {
|
|
||||||
style += 'top:50%;transform:translateY(-50%);';
|
|
||||||
} else {
|
|
||||||
style += 'top:20%;';
|
|
||||||
}
|
|
||||||
|
|
||||||
// #ifdef H5
|
|
||||||
style += 'padding-top: ' + (ratio * 100) + '%;';
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
style += 'padding-top: ' + (ratio * 100) + '%;';
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifdef MP-HARMONY
|
|
||||||
style += 'height: ' + (this.windowWidth * ratio) + 'px;';
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 显示视频播放弹窗
|
// 显示视频播放弹窗
|
||||||
showVideo(item) {
|
showVideo(item) {
|
||||||
this.video_url = item.videoUrl;
|
let url = item.videoUrl || '';
|
||||||
|
// 处理视频URL:完整链接直接使用,否则通过 $util.img() 转换
|
||||||
|
if (url && url.indexOf('http://') !== 0 && url.indexOf('https://') !== 0) {
|
||||||
|
url = this.$util.img(url);
|
||||||
|
}
|
||||||
|
this.video_url = url;
|
||||||
this.popupVideoWidth = 0;
|
this.popupVideoWidth = 0;
|
||||||
this.popupVideoHeight = 0;
|
this.popupVideoHeight = 0;
|
||||||
this.$refs.videoPopup.open();
|
this.$refs.videoPopup.open();
|
||||||
@@ -625,22 +609,6 @@ export default {
|
|||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #ifdef MP-WEIXIN */
|
|
||||||
.adaptive-video {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
/* #endif */
|
|
||||||
|
|
||||||
/* #ifdef MP-HARMONY */
|
|
||||||
.adaptive-video {
|
|
||||||
position: static;
|
|
||||||
display: block;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
/* #endif */
|
|
||||||
|
|
||||||
.video-nav {
|
.video-nav {
|
||||||
padding: 16rpx;
|
padding: 16rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
:style="videoWarpCss"
|
:style="videoWarpCss"
|
||||||
controls
|
controls
|
||||||
preload="metadata"
|
preload="metadata"
|
||||||
|
playsinline
|
||||||
|
webkit-playsinline
|
||||||
|
x5-playsinline
|
||||||
@loadedmetadata="onVideoLoaded"
|
@loadedmetadata="onVideoLoaded"
|
||||||
@click="handlerClick(videoUrl)"
|
@click="handlerClick(videoUrl)"
|
||||||
></video>
|
></video>
|
||||||
@@ -106,11 +109,11 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// #ifdef H5
|
// #ifdef H5
|
||||||
style += 'padding-top: ' + (ratio * 100) + '%;';
|
style += 'height:0;padding-top: ' + (ratio * 100) + '%;';
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
style += 'padding-top: ' + (ratio * 100) + '%;';
|
style += 'height:0;padding-top: ' + (ratio * 100) + '%;';
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifdef MP-HARMONY
|
// #ifdef MP-HARMONY
|
||||||
|
|||||||
@@ -26,38 +26,17 @@
|
|||||||
<text class="required" :style="{ color: requiredColor }">{{ item.value.required ? '*' : '' }}</text>
|
<text class="required" :style="{ color: requiredColor }">{{ item.value.required ? '*' : '' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="box">
|
<view class="box">
|
||||||
<!-- #ifdef MP-HARMONY -->
|
<!-- 所有平台统一通过 formConfig.textareaRows / item.value.rows 控制显示行数:
|
||||||
<!-- 鸿蒙元服务渲染为原生 ArkUI TextArea,必须用 :rows 控制初始显示行数;CSS height 在原生组件上不生效 -->
|
- H5 / 微信:原生 textarea 识别 :rows 属性,可视高度 = rows × line-height(CSS 控制 line-height);
|
||||||
|
- 鸿蒙:渲染为原生 ArkUI TextArea,CSS height 不生效,必须靠 :rows 控制。
|
||||||
|
行数和行高全部由 formConfig 动态下发,不再硬编码 176rpx / row=2。 -->
|
||||||
<textarea
|
<textarea
|
||||||
:rows="item.value.rows || 2"
|
:rows="item.value.rows || textareaRowsValue"
|
||||||
:placeholder="item.value.placeholder"
|
:placeholder="item.value.placeholder"
|
||||||
placeholder-class="placeholder color-tip"
|
placeholder-class="placeholder color-tip"
|
||||||
v-model="item.val"
|
v-model="item.val"
|
||||||
:style="inputStyle"
|
:style="textareaStyle"
|
||||||
></textarea>
|
></textarea>
|
||||||
<!-- #endif -->
|
|
||||||
<!-- #ifndef MP-HARMONY -->
|
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
|
||||||
<!-- 微信小程序原生 textarea:
|
|
||||||
不用 auto-height(会随内容无限扩展遮挡后续组件),改用 textareaStyle 中
|
|
||||||
固定 height 176rpx(约 4 行)+ line-height 44rpx,可视区域比原 88rpx 翻倍,
|
|
||||||
内容多时内部滚动条 thumb 比例改善一倍,且高度可控。 -->
|
|
||||||
<textarea
|
|
||||||
:placeholder="item.value.placeholder"
|
|
||||||
placeholder-class="placeholder color-tip"
|
|
||||||
v-model="item.val"
|
|
||||||
:style="textareaStyle"
|
|
||||||
></textarea>
|
|
||||||
<!-- #endif -->
|
|
||||||
<!-- #ifndef MP-WEIXIN -->
|
|
||||||
<textarea
|
|
||||||
:placeholder="item.value.placeholder"
|
|
||||||
placeholder-class="placeholder color-tip"
|
|
||||||
v-model="item.val"
|
|
||||||
:style="inputStyle"
|
|
||||||
></textarea>
|
|
||||||
<!-- #endif -->
|
|
||||||
<!-- #endif -->
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -301,6 +280,11 @@
|
|||||||
checkedColor: '#ff4544',
|
checkedColor: '#ff4544',
|
||||||
placeholderColor: '#909399',
|
placeholderColor: '#909399',
|
||||||
placeholderFontSize: 28,
|
placeholderFontSize: 28,
|
||||||
|
// textarea 显示行数 / 行高(与 ns-newform 对齐)
|
||||||
|
// 改用 :rows + line-height 驱动高度:rows 给原生 <textarea>(H5/微信原生层/鸿蒙 ArkUI 都识别),
|
||||||
|
// line-height 用于在 textareaStyle 里算出对应的 px 高度;不再写死 176rpx。
|
||||||
|
textareaRows: 4,
|
||||||
|
textareaLineHeight: 44,
|
||||||
// 输入框视觉增强(与 ns-newform 对齐)
|
// 输入框视觉增强(与 ns-newform 对齐)
|
||||||
inputBorderColor: '#e5e7eb',
|
inputBorderColor: '#e5e7eb',
|
||||||
inputBgColor: '#f9fafb',
|
inputBgColor: '#f9fafb',
|
||||||
@@ -334,6 +318,8 @@
|
|||||||
checkedColor: pick('checkedColor', 'checked_color'),
|
checkedColor: pick('checkedColor', 'checked_color'),
|
||||||
placeholderColor: pick('placeholderColor', 'placeholder_color'),
|
placeholderColor: pick('placeholderColor', 'placeholder_color'),
|
||||||
placeholderFontSize: pick('placeholderFontSize', 'placeholder_font_size'),
|
placeholderFontSize: pick('placeholderFontSize', 'placeholder_font_size'),
|
||||||
|
textareaRows: pick('textareaRows', 'textarea_rows'),
|
||||||
|
textareaLineHeight: pick('textareaLineHeight', 'textarea_line_height'),
|
||||||
inputBorderColor: pick('inputBorderColor', 'input_border_color'),
|
inputBorderColor: pick('inputBorderColor', 'input_border_color'),
|
||||||
inputBgColor: pick('inputBgColor', 'input_bg_color'),
|
inputBgColor: pick('inputBgColor', 'input_bg_color'),
|
||||||
inputFocusBorderColor: pick('inputFocusBorderColor', 'input_focus_border_color'),
|
inputFocusBorderColor: pick('inputFocusBorderColor', 'input_focus_border_color'),
|
||||||
@@ -394,6 +380,8 @@
|
|||||||
'--checked-color': cfg.checkedColor,
|
'--checked-color': cfg.checkedColor,
|
||||||
'--placeholder-color': cfg.placeholderColor,
|
'--placeholder-color': cfg.placeholderColor,
|
||||||
'--placeholder-font-size': this.$util.rpx(num(cfg.placeholderFontSize, 28)),
|
'--placeholder-font-size': this.$util.rpx(num(cfg.placeholderFontSize, 28)),
|
||||||
|
'--textarea-rows': num(cfg.textareaRows, 4),
|
||||||
|
'--textarea-line-height': this.$util.rpx(num(cfg.textareaLineHeight, 44)),
|
||||||
// 输入框视觉增强(与 ns-newform 对齐)
|
// 输入框视觉增强(与 ns-newform 对齐)
|
||||||
'--input-border-color': cfg.inputBorderColor,
|
'--input-border-color': cfg.inputBorderColor,
|
||||||
'--input-bg-color': cfg.inputBgColor,
|
'--input-bg-color': cfg.inputBgColor,
|
||||||
@@ -434,21 +422,37 @@
|
|||||||
'line-height': this.$util.rpx(this.config.inputHeight)
|
'line-height': this.$util.rpx(this.config.inputHeight)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// textarea 专用 inline style(仅微信小程序端使用,见 template 中 #ifdef MP-WEIXIN 分支)
|
// textarea 显示行数(来自 formConfig.textareaRows,未配置时回退到默认 4)
|
||||||
|
textareaRowsValue() {
|
||||||
|
const n = parseInt(this.config.textareaRows, 10);
|
||||||
|
return Number.isFinite(n) && n > 0 ? n : 4;
|
||||||
|
},
|
||||||
|
// textarea 行高(来自 formConfig.textareaLineHeight,用于 :rows × line-height 算出固定可视高度)
|
||||||
|
textareaLineHeightValue() {
|
||||||
|
const n = parseInt(this.config.textareaLineHeight, 10);
|
||||||
|
return Number.isFinite(n) && n > 0 ? n : 44;
|
||||||
|
},
|
||||||
|
// textarea 专用 inline style(所有平台统一使用,见 template 中 textarea 节点)
|
||||||
// 设计要点:
|
// 设计要点:
|
||||||
// 1) 不复用 inputStyle 的 min-height / line-height:这两个值取自 inputHeight(默认 60rpx),
|
// 1) 不复用 inputStyle 的 min-height / line-height:那两个值取自 inputHeight(默认 60rpx),
|
||||||
// 对 textarea 太小,会让微信小程序原生 textarea 单行过高、可视区域过小,
|
// 对 textarea 太小,会让原生 textarea 单行过高、可视区域过小;
|
||||||
// 内容稍多即溢出导致原生滚动条 thumb 异常小;
|
// 2) 不使用 auto-height:原生 textarea 上 auto-height 会让组件随内容无限扩展,
|
||||||
// 2) 不使用 auto-height:在微信小程序原生 textarea 上,auto-height 会让 textarea 随内容
|
// 遮挡后续组件,导致"看不到下面的其他组件";
|
||||||
// 无限扩展,遮挡后续组件,导致"看不到下面的其他组件";
|
// 3) 高度 = rows × lineHeight(两个值都由 formConfig 动态下发,不再写死 176rpx):
|
||||||
// 3) 改用固定 height 176rpx(约 4 行,line-height 44rpx),相比原 CSS height: 88rpx(2 行)
|
// - H5 浏览器 / 微信原生层:原生 <textarea> 同时识别 :rows 和 CSS line-height,
|
||||||
// 可视区域翻倍,内容多时内部滚动条 thumb 比例改善一倍,且高度可控不会遮挡后续组件。
|
// 高度由浏览器自然算出,与 :rows 完全一致;
|
||||||
|
// - 鸿蒙 ArkUI:原生 TextArea 只识别 :rows,不识别 CSS height,所以我们在 JS 里
|
||||||
|
// 显式算 height = rows × lineHeight 反向写出,保证三端可视高度像素级一致。
|
||||||
textareaStyle() {
|
textareaStyle() {
|
||||||
const h = this.$util.rpx(176);
|
const lh = this.$util.rpx(this.textareaLineHeightValue);
|
||||||
|
const rows = this.textareaRowsValue;
|
||||||
|
// 加上 padding(12rpx 上下) 和 border(1rpx 上下) 的占用,避免与 :rows 算出的高度不一致时被截断
|
||||||
|
const paddings = this.$util.rpx(24) + this.$util.rpx(2);
|
||||||
|
const h = (this.$util.rpx(this.textareaLineHeightValue * (rows - 1)) + lh) + paddings;
|
||||||
return this.$util.objToStyle({
|
return this.$util.objToStyle({
|
||||||
'height': h,
|
'height': h,
|
||||||
'min-height': h,
|
'min-height': h,
|
||||||
'line-height': this.$util.rpx(44),
|
'line-height': lh,
|
||||||
'border-radius': this.$util.rpx(this.config.inputRadius),
|
'border-radius': this.$util.rpx(this.config.inputRadius),
|
||||||
'font-size': this.$util.rpx(this.config.labelFontSize),
|
'font-size': this.$util.rpx(this.config.labelFontSize),
|
||||||
'color': this.config.labelColor
|
'color': this.config.labelColor
|
||||||
@@ -959,6 +963,9 @@
|
|||||||
--placeholder-font-size: 28rpx;
|
--placeholder-font-size: 28rpx;
|
||||||
--checked-color: #ff4544;
|
--checked-color: #ff4544;
|
||||||
--placeholder-color: #909399;
|
--placeholder-color: #909399;
|
||||||
|
// textarea 行数 / 行高(运行时被 formWrapStyle 注入的 inline 覆盖;这里是兜底)
|
||||||
|
--textarea-rows: 4;
|
||||||
|
--textarea-line-height: 44rpx;
|
||||||
// 输入框视觉增强(与 ns-newform 对齐)
|
// 输入框视觉增强(与 ns-newform 对齐)
|
||||||
--input-border-color: #e5e7eb;
|
--input-border-color: #e5e7eb;
|
||||||
--input-bg-color: #f9fafb;
|
--input-bg-color: #f9fafb;
|
||||||
@@ -1094,11 +1101,12 @@
|
|||||||
font-size: var(--label-font-size, $font-size-base);
|
font-size: var(--label-font-size, $font-size-base);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: var(--label-color, #1f2937);
|
color: var(--label-color, #1f2937);
|
||||||
// 鸿蒙元服务渲染为原生 ArkUI TextArea,不识别 CSS height,由 :rows 控制行数;
|
// 行高由 formConfig.textareaLineHeight(兜底 44rpx)控制;
|
||||||
// 这里给所有端都设一个最小高度基线,避免空内容时塌缩
|
// 高度 = rows × line-height + padding + border,所有值都从 CSS 变量读取,
|
||||||
min-height: 88rpx;
|
// 真正做到了"rows 改 → 高度自动改"。
|
||||||
height: 88rpx;
|
line-height: var(--textarea-line-height, 44rpx);
|
||||||
line-height: 44rpx;
|
min-height: calc(var(--textarea-rows, 4) * var(--textarea-line-height, 44rpx) + 26rpx);
|
||||||
|
height: calc(var(--textarea-rows, 4) * var(--textarea-line-height, 44rpx) + 26rpx);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border-radius: var(--input-radius, 0rpx);
|
border-radius: var(--input-radius, 0rpx);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|||||||
@@ -27,10 +27,16 @@
|
|||||||
<text class="required" :style="{ color: requiredColor }">{{ item.value.required ? '*' : '' }}</text>
|
<text class="required" :style="{ color: requiredColor }">{{ item.value.required ? '*' : '' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view :class="['box', 'align-left']" :style="item._boxAlignStyle">
|
<view :class="['box', 'align-left']" :style="item._boxAlignStyle">
|
||||||
|
<!-- 所有平台统一通过 formConfig.textareaRows / item.value.rows 控制显示行数:
|
||||||
|
- H5 / 微信:原生 textarea 识别 :rows 属性;
|
||||||
|
- 鸿蒙:渲染为 ArkUI 原生 TextArea,CSS height 不生效,必须靠 :rows 控制。
|
||||||
|
rows 全部由 formConfig 动态下发,与 ns-form 行为保持一致。 -->
|
||||||
<textarea
|
<textarea
|
||||||
|
:rows="item.value.rows || textareaRowsValue"
|
||||||
:placeholder="item.value.placeholder"
|
:placeholder="item.value.placeholder"
|
||||||
placeholder-class="placeholder color-tip"
|
placeholder-class="placeholder color-tip"
|
||||||
v-model="item.val"
|
v-model="item.val"
|
||||||
|
:style="textareaStyle"
|
||||||
></textarea>
|
></textarea>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -377,6 +383,10 @@ export default {
|
|||||||
checkedColor: '#ff4544',
|
checkedColor: '#ff4544',
|
||||||
placeholderColor: '#ddd',
|
placeholderColor: '#ddd',
|
||||||
placeholderFontSize: 28,
|
placeholderFontSize: 28,
|
||||||
|
// textarea 显示行数 / 行高(与 ns-form 保持一致,由 formConfig 覆盖)
|
||||||
|
// 改用 :rows + line-height 驱动高度,rows 给原生 <textarea>,line-height 在 textareaStyle 算 px 高度
|
||||||
|
textareaRows: 4,
|
||||||
|
textareaLineHeight: 44,
|
||||||
// 输入框视觉增强(可由后端 form_config 覆盖)
|
// 输入框视觉增强(可由后端 form_config 覆盖)
|
||||||
inputBorderColor: '#e5e7eb',
|
inputBorderColor: '#e5e7eb',
|
||||||
inputBgColor: '#f9fafb',
|
inputBgColor: '#f9fafb',
|
||||||
@@ -452,6 +462,9 @@ export default {
|
|||||||
'--checked-color': cfg.checkedColor,
|
'--checked-color': cfg.checkedColor,
|
||||||
'--placeholder-color': cfg.placeholderColor,
|
'--placeholder-color': cfg.placeholderColor,
|
||||||
'--placeholder-font-size': this.$util.rpx(num(cfg.placeholderFontSize, 28)),
|
'--placeholder-font-size': this.$util.rpx(num(cfg.placeholderFontSize, 28)),
|
||||||
|
// textarea 行数 / 行高(CSS 兜底 + JS 算高度都用得到)
|
||||||
|
'--textarea-rows': num(cfg.textareaRows, 4),
|
||||||
|
'--textarea-line-height': this.$util.rpx(num(cfg.textareaLineHeight, 44)),
|
||||||
// 输入框视觉增强(边框 / 背景 / 聚焦 / label 字重)
|
// 输入框视觉增强(边框 / 背景 / 聚焦 / label 字重)
|
||||||
'--input-border-color': cfg.inputBorderColor,
|
'--input-border-color': cfg.inputBorderColor,
|
||||||
'--input-bg-color': cfg.inputBgColor,
|
'--input-bg-color': cfg.inputBgColor,
|
||||||
@@ -497,6 +510,30 @@ export default {
|
|||||||
'border-radius': this.$util.rpx(this.config.inputRadius)
|
'border-radius': this.$util.rpx(this.config.inputRadius)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// textarea 显示行数(来自 formConfig.textareaRows,未配置时回退到默认 4)
|
||||||
|
textareaRowsValue() {
|
||||||
|
const n = parseInt(this.config.textareaRows, 10);
|
||||||
|
return Number.isFinite(n) && n > 0 ? n : 4;
|
||||||
|
},
|
||||||
|
// textarea 行高(来自 formConfig.textareaLineHeight,用于 :rows × line-height 算出可视高度)
|
||||||
|
textareaLineHeightValue() {
|
||||||
|
const n = parseInt(this.config.textareaLineHeight, 10);
|
||||||
|
return Number.isFinite(n) && n > 0 ? n : 44;
|
||||||
|
},
|
||||||
|
// textarea 专用 inline style(所有平台统一使用,见 template 中 textarea 节点)
|
||||||
|
// 高度 = rows × lineHeight + padding + border,由 formConfig 动态控制,与 ns-form 同公式
|
||||||
|
textareaStyle() {
|
||||||
|
const lh = this.$util.rpx(this.textareaLineHeightValue);
|
||||||
|
const rows = this.textareaRowsValue;
|
||||||
|
const paddings = this.$util.rpx(24) + this.$util.rpx(2); // padding 12rpx 上下 + border 1rpx 上下
|
||||||
|
const h = (this.$util.rpx(this.textareaLineHeightValue * (rows - 1)) + lh) + paddings;
|
||||||
|
return this.$util.objToStyle({
|
||||||
|
'height': h,
|
||||||
|
'min-height': h,
|
||||||
|
'line-height': lh,
|
||||||
|
'border-radius': this.$util.rpx(this.config.inputRadius)
|
||||||
|
});
|
||||||
|
},
|
||||||
labelStyle() {
|
labelStyle() {
|
||||||
const style = {
|
const style = {
|
||||||
fontSize: this.$util.rpx(this.config.labelFontSize),
|
fontSize: this.$util.rpx(this.config.labelFontSize),
|
||||||
@@ -997,6 +1034,9 @@ export default {
|
|||||||
--submit-bg-color: #ff4544;
|
--submit-bg-color: #ff4544;
|
||||||
--placeholder-color: #ddd;
|
--placeholder-color: #ddd;
|
||||||
--placeholder-font-size: 28rpx;
|
--placeholder-font-size: 28rpx;
|
||||||
|
// textarea 行数 / 行高(运行时被 formWrapStyle 注入的 inline 覆盖;这里是兜底)
|
||||||
|
--textarea-rows: 4;
|
||||||
|
--textarea-line-height: 44rpx;
|
||||||
// 输入框视觉增强(边框 / 背景 / 聚焦 / label 字重)
|
// 输入框视觉增强(边框 / 背景 / 聚焦 / label 字重)
|
||||||
--input-border-color: #e5e7eb;
|
--input-border-color: #e5e7eb;
|
||||||
--input-bg-color: #f9fafb;
|
--input-bg-color: #f9fafb;
|
||||||
@@ -1234,8 +1274,10 @@ export default {
|
|||||||
::v-deep textarea {
|
::v-deep textarea {
|
||||||
font-size: var(--label-font-size, 28rpx);
|
font-size: var(--label-font-size, 28rpx);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 120rpx;
|
// 行高 / 高度全部由 formConfig 动态决定(CSS 兜底公式与 JS textareaStyle 一致)
|
||||||
line-height: 1.5;
|
line-height: var(--textarea-line-height, 44rpx);
|
||||||
|
min-height: calc(var(--textarea-rows, 4) * var(--textarea-line-height, 44rpx) + 26rpx);
|
||||||
|
height: calc(var(--textarea-rows, 4) * var(--textarea-line-height, 44rpx) + 26rpx);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border: 1rpx solid var(--input-border-color, #e5e7eb);
|
border: 1rpx solid var(--input-border-color, #e5e7eb);
|
||||||
border-radius: var(--input-radius, 10rpx);
|
border-radius: var(--input-radius, 10rpx);
|
||||||
@@ -1244,7 +1286,6 @@ export default {
|
|||||||
color: var(--label-color, #1f2937);
|
color: var(--label-color, #1f2937);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: auto;
|
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
background: var(--input-focus-bg-color, #ffffff);
|
background: var(--input-focus-bg-color, #ffffff);
|
||||||
@@ -1260,8 +1301,10 @@ export default {
|
|||||||
textarea {
|
textarea {
|
||||||
font-size: var(--label-font-size, 28rpx);
|
font-size: var(--label-font-size, 28rpx);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 120rpx;
|
// 行高 / 高度全部由 formConfig 动态决定(与 ::v-deep textarea 同步)
|
||||||
line-height: 1.5;
|
line-height: var(--textarea-line-height, 44rpx);
|
||||||
|
min-height: calc(var(--textarea-rows, 4) * var(--textarea-line-height, 44rpx) + 26rpx);
|
||||||
|
height: calc(var(--textarea-rows, 4) * var(--textarea-line-height, 44rpx) + 26rpx);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border: 1rpx solid var(--input-border-color, #e5e7eb);
|
border: 1rpx solid var(--input-border-color, #e5e7eb);
|
||||||
border-radius: var(--input-radius, 10rpx);
|
border-radius: var(--input-radius, 10rpx);
|
||||||
@@ -1270,7 +1313,6 @@ export default {
|
|||||||
color: var(--label-color, #1f2937);
|
color: var(--label-color, #1f2937);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: auto;
|
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
background: var(--input-focus-bg-color, #ffffff);
|
background: var(--input-focus-bg-color, #ffffff);
|
||||||
|
|||||||
@@ -9,9 +9,13 @@
|
|||||||
<view class="video-popup-body">
|
<view class="video-popup-body">
|
||||||
<video
|
<video
|
||||||
v-if="videoUrl"
|
v-if="videoUrl"
|
||||||
|
id="popupVideoPlayer"
|
||||||
:src="videoUrl"
|
:src="videoUrl"
|
||||||
controls
|
controls
|
||||||
autoplay
|
autoplay
|
||||||
|
playsinline
|
||||||
|
webkit-playsinline
|
||||||
|
x5-playsinline
|
||||||
class="video-player"
|
class="video-player"
|
||||||
></video>
|
></video>
|
||||||
<view v-else class="video-error">{{ $lang('components.ns-video-player-popup.videoUrlNotExist') }}</view>
|
<view v-else class="video-error">{{ $lang('components.ns-video-player-popup.videoUrlNotExist') }}</view>
|
||||||
@@ -22,6 +26,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { playVideoInContext } from '@/common/js/video-helper.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ns-video-player-popup',
|
name: 'ns-video-player-popup',
|
||||||
props: {
|
props: {
|
||||||
@@ -76,6 +82,10 @@ export default {
|
|||||||
* @param {Object} e - 弹窗状态变化事件
|
* @param {Object} e - 弹窗状态变化事件
|
||||||
*/
|
*/
|
||||||
onVideoPopupChange(e) {
|
onVideoPopupChange(e) {
|
||||||
|
// H5 端弹窗显示后触发播放:避免 autoplay 被浏览器拦截 / 弹窗动画未完成被 pause() 打断
|
||||||
|
if (e && e.show) {
|
||||||
|
playVideoInContext('popupVideoPlayer', this);
|
||||||
|
}
|
||||||
this.$emit('popup-change', e);
|
this.$emit('popup-change', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,4 @@ export const lang = {
|
|||||||
// Modal dialog
|
// Modal dialog
|
||||||
tip: 'Tip',
|
tip: 'Tip',
|
||||||
paramError: 'Parameter error',
|
paramError: 'Parameter error',
|
||||||
// Back to home
|
|
||||||
backHome: 'Back to Home',
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,4 @@ export const lang = {
|
|||||||
// 弹窗
|
// 弹窗
|
||||||
tip: '提示',
|
tip: '提示',
|
||||||
paramError: '参数错误',
|
paramError: '参数错误',
|
||||||
// 返回主页
|
|
||||||
backHome: '返回主页',
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2152,7 +2152,9 @@
|
|||||||
{
|
{
|
||||||
"path": "form/formdata",
|
"path": "form/formdata",
|
||||||
"style": {
|
"style": {
|
||||||
// #ifdef APP-PLUS
|
// APP 端原本就是 custom;微信小程序 / 华为快应用 / 鸿蒙元服务
|
||||||
|
// 的原生 navbar 不支持自定义左侧 Home 按钮,需要切到 custom 自绘
|
||||||
|
// #ifdef APP-PLUS || MP
|
||||||
"navigationStyle": "custom",
|
"navigationStyle": "custom",
|
||||||
// #endif
|
// #endif
|
||||||
"navigationBarTitleText": "",
|
"navigationBarTitleText": "",
|
||||||
|
|||||||
@@ -59,7 +59,8 @@
|
|||||||
<!-- 商品视频 -->
|
<!-- 商品视频 -->
|
||||||
<view class="goods-video" :class="{ show: switchMedia == 'video' }">
|
<view class="goods-video" :class="{ show: switchMedia == 'video' }">
|
||||||
<video id="goodsVideo" :src="$util.img(goodsSkuDetail.video_url)"
|
<video id="goodsVideo" :src="$util.img(goodsSkuDetail.video_url)"
|
||||||
:poster="$util.img(goodsSkuDetail.sku_image, { size: 'big' })" objectFit="cover"></video>
|
:poster="$util.img(goodsSkuDetail.sku_image, { size: 'big' })" objectFit="cover"
|
||||||
|
playsinline webkit-playsinline x5-playsinline></video>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 切换视频、图片 -->
|
<!-- 切换视频、图片 -->
|
||||||
|
|||||||
@@ -1,16 +1,34 @@
|
|||||||
<template>
|
<template>
|
||||||
<view :style="themeColor">
|
<view :style="themeColor">
|
||||||
<!-- 返回主页按钮:左上角固定,始终可见 -->
|
<!--
|
||||||
<view class="home-btn" @click="goHome">
|
自绘导航栏(仅在 navigationStyle:custom 的端渲染:APP / 微信小程序 / 华为快应用 / 鸿蒙元服务)
|
||||||
<text class="home-btn-text">{{ $lang('form.formdata.backHome') }}</text>
|
- pages.json 已为这几端开启 navigationStyle:custom,系统不再提供原生 navbar
|
||||||
|
- 左侧:hasBackStack 为 true 时显示返回箭头(点击 navigateBack),showHomeBtn=true 时再叠加 Home 胶囊
|
||||||
|
- 中央:表单标题(form_name)
|
||||||
|
- 整体高度 = 状态栏高度 + 44px 内容区,由 customNavbarStyle 动态算
|
||||||
|
H5 端保留系统原生 navbar,不渲染此块
|
||||||
|
-->
|
||||||
|
<!-- #ifndef H5 -->
|
||||||
|
<view class="form-home-navbar" :style="customNavbarStyle">
|
||||||
|
<view class="form-home-navbar__left">
|
||||||
|
<view v-if="hasBackStack" class="form-home-navbar__back" @tap="goBack" hover-class="form-home-navbar__btn--hover">
|
||||||
|
<text class="form-home-navbar__back-icon">‹</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="showHomeBtn" class="form-home-capsule" @tap="goHome" hover-class="form-home-capsule--hover">
|
||||||
|
<text class="iconfont icon-shouye1 form-home-capsule__icon"></text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="form-home-navbar__title">{{ navbarTitle }}</view>
|
||||||
|
<view class="form-home-navbar__placeholder" />
|
||||||
</view>
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
<block v-if="detail">
|
<block v-if="detail">
|
||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="system-form-wrap">
|
<view class="system-form-wrap">
|
||||||
<!-- 复用 diy-form:banner / tip / 表单字段 / 提交按钮 全部由其内部渲染,样式(bannerPaddingX/tipColor/submitBgColor 等)由 form_config 控制。
|
<!-- 复用 diy-form:banner / tip / 表单字段 / 提交按钮 全部由其内部渲染,样式(bannerPaddingX/tipColor/submitBgColor 等)由 form_config 控制。
|
||||||
:onSubmit 接管提交逻辑(带 uniacid + showModal);跨 H5 / MP 一致,不依赖 $listeners。
|
:onSubmit 接管提交逻辑(带 uniacid + showModal);跨 H5 / MP 一致,不依赖 $listeners。
|
||||||
注意:create 重度依赖本组件 this(id/uniacid/memberInfo/$api 等),必须在 created 阶段 bind this,
|
注意:create 重度依赖本组件 this(id/uniacid/memberInfo/$api 等),必须在 created 阶段 bind this,
|
||||||
否则 diy-form 通过 this.onSubmit(formData) 调用时 this 会漂移到子组件实例 -->
|
否则 diy-form 通过 this.onSubmit(formData) 调用时 this 会漂移到子组件实例 -->
|
||||||
<diy-form ref="form" :value="diyValue" dataMode="raw" :onSubmit="handleSubmit" />
|
<diy-form ref="form" :value="diyValue" dataMode="raw" :onSubmit="handleSubmit" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -27,13 +45,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import diyForm from '@/components-diy/diy-form.vue';
|
|
||||||
import scroll from '@/common/js/scroll-view.js';
|
import scroll from '@/common/js/scroll-view.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
|
||||||
diyForm
|
|
||||||
},
|
|
||||||
mixins: [scroll],
|
mixins: [scroll],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -45,9 +59,17 @@ export default {
|
|||||||
uniacid: 0,
|
uniacid: 0,
|
||||||
isFromMpWebView: false, // 是否是从微信小程序的WebView跳转过来的
|
isFromMpWebView: false, // 是否是从微信小程序的WebView跳转过来的
|
||||||
hideNavbar: false, // 查询参数中支持隐藏Navbar
|
hideNavbar: false, // 查询参数中支持隐藏Navbar
|
||||||
|
showHomeBtn: false, // 查询参数 show_home=1 时,在系统标题栏左侧显示 Home 按钮
|
||||||
urlParams: {}, // 通用 URL 透传参数收集
|
urlParams: {}, // 通用 URL 透传参数收集
|
||||||
handleSubmit: null, // create 的 this 绑定版本,作为 :onSubmit 传给 diy-form(见 created)
|
handleSubmit: null, // create 的 this 绑定版本,作为 :onSubmit 传给 diy-form(见 created)
|
||||||
|
|
||||||
|
// #ifndef H5
|
||||||
|
navbarHeight: 44, // 自绘 navbar 内容区高度(不含状态栏),跨端兜底 44px
|
||||||
|
statusBarHeight: 20, // 系统状态栏高度,自绘 navbar 时需要预留顶部空隙
|
||||||
|
navbarTitle: '', // 自绘 navbar 中央显示的标题
|
||||||
|
hasBackStack: false, // 自绘 navbar 左上角是否显示返回箭头:页面栈 > 1 时显示
|
||||||
|
// #endif
|
||||||
|
|
||||||
// #ifdef H5
|
// #ifdef H5
|
||||||
formTitle: '', // 查询参数传入的Title
|
formTitle: '', // 查询参数传入的Title
|
||||||
enableSetTitle: false, // 查询参数传入的是否可以强制设置Title
|
enableSetTitle: false, // 查询参数传入的是否可以强制设置Title
|
||||||
@@ -62,6 +84,22 @@ export default {
|
|||||||
this.handleSubmit = this.create.bind(this);
|
this.handleSubmit = this.create.bind(this);
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
// #ifndef H5
|
||||||
|
// 自绘 navbar 的高度样式
|
||||||
|
// - 内容区高度 = navbarHeight(兜底 44px)
|
||||||
|
// - 整体高度 = 状态栏高度 + 内容区高度
|
||||||
|
// - padding-top 把内容区下移到状态栏之下,避免被刘海/水滴遮挡
|
||||||
|
customNavbarStyle() {
|
||||||
|
try {
|
||||||
|
const sys = uni.getSystemInfoSync();
|
||||||
|
const sb = sys.statusBarHeight || 0;
|
||||||
|
const h = this.navbarHeight || 44;
|
||||||
|
return `height: ${sb + h}px; padding-top: ${sb}px;`;
|
||||||
|
} catch (e) {
|
||||||
|
return 'height: 64px; padding-top: 20px;';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// #endif
|
||||||
// 构造 diy-form raw 模式期望的 value 形态:
|
// 构造 diy-form raw 模式期望的 value 形态:
|
||||||
// - jsonData:透传给 ns-newform(已是 ns-newform 期望的 [{ controller, value, val }])
|
// - jsonData:透传给 ns-newform(已是 ns-newform 期望的 [{ controller, value, val }])
|
||||||
// - formConfig:后端返回 camelCase(如 formPaddingX / formBgColor / labelFontSize),
|
// - formConfig:后端返回 camelCase(如 formPaddingX / formBgColor / labelFontSize),
|
||||||
@@ -102,11 +140,36 @@ export default {
|
|||||||
this.uniacid = options.uniacid
|
this.uniacid = options.uniacid
|
||||||
this.id = options.id || 0
|
this.id = options.id || 0
|
||||||
this.isFromMpWebView = options.from === 'mp-webview';
|
this.isFromMpWebView = options.from === 'mp-webview';
|
||||||
this.hideNavbar = Boolean(options?.hide_navbar);
|
this.hideNavbar = this.$util.coerceBool(options?.hide_navbar);
|
||||||
|
// show_home=1 时在系统标题栏左侧显示 Home 按钮,点击回到首页 INDEX_PAGE_URL
|
||||||
|
// 解析规则统一走 this.$util.coerceBool:支持 '1' / 'true' / 1 / true 四种写法
|
||||||
|
// 仅在 navigationStyle:custom 端生效(APP / 微信小程序 / 华为快应用 / 鸿蒙元服务);
|
||||||
|
// H5 保留系统原生 navbar,不支持 show_home,参数被忽略(这是预期行为)
|
||||||
|
// 注:H5 嵌入微信小程序 webview(from=mp-webview)的场景也属于 H5 范畴,
|
||||||
|
// 此场景下也没有 Home 入口——后续如需支持,需要单独处理(导航靠宿主小程序接管)
|
||||||
|
this.showHomeBtn = this.$util.coerceBool(options?.show_home);
|
||||||
|
|
||||||
|
// #ifndef H5
|
||||||
|
// 自绘 navbar 需要用到状态栏高度 / navbar 内容区高度(跨端兜底)
|
||||||
|
try {
|
||||||
|
const sys = uni.getSystemInfoSync();
|
||||||
|
if (sys.statusBarHeight) this.statusBarHeight = sys.statusBarHeight;
|
||||||
|
// uniapp 在不同端 menuButton / navigationBar 高度略有差异,给个安全兜底即可
|
||||||
|
if (typeof sys.navbarHeight === 'number' && sys.navbarHeight > 0) {
|
||||||
|
this.navbarHeight = sys.navbarHeight;
|
||||||
|
}
|
||||||
|
} catch (e) { /* keep defaults */ }
|
||||||
|
|
||||||
|
// 自绘 navbar 是否显示返回箭头:getCurrentPages().length > 1 即页面有栈
|
||||||
|
try {
|
||||||
|
const pages = getCurrentPages ? getCurrentPages() : [];
|
||||||
|
this.hasBackStack = Array.isArray(pages) && pages.length > 1;
|
||||||
|
} catch (e) { /* keep default */ }
|
||||||
|
// #endif
|
||||||
|
|
||||||
// 通用 URL 参数收集:除业务保留字段外,其余全部透传给后端
|
// 通用 URL 参数收集:除业务保留字段外,其余全部透传给后端
|
||||||
// 后端会做卫生过滤并广播事件,供 recommend 等插件消费
|
// 后端会做卫生过滤并广播事件,供 recommend 等插件消费
|
||||||
const reservedKeys = ['uniacid', 'id', 'scene', 'from', 'hide_navbar', 'title', 'enable_set_title'];
|
const reservedKeys = ['uniacid', 'id', 'scene', 'from', 'hide_navbar', 'show_home', 'title', 'enable_set_title'];
|
||||||
this.urlParams = {};
|
this.urlParams = {};
|
||||||
for (let key in options) {
|
for (let key in options) {
|
||||||
if (reservedKeys.indexOf(key) !== -1) continue;
|
if (reservedKeys.indexOf(key) !== -1) continue;
|
||||||
@@ -149,6 +212,20 @@ export default {
|
|||||||
this.updateNavbarTitle();
|
this.updateNavbarTitle();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// #ifndef H5
|
||||||
|
// 返回上一页;用 navigateBack,栈深度>1 时由 hasBackStack 控制按钮可见性
|
||||||
|
goBack() {
|
||||||
|
uni.navigateBack({ delta: 1 });
|
||||||
|
},
|
||||||
|
// #endif
|
||||||
|
// 返回首页;
|
||||||
|
// INDEX_PAGE_URL 是 TabBar 根页面,不能用裸 uni.redirectTo(会报"can not redirectTo a tabBar page")
|
||||||
|
// 必须用 this.$util.redirectTo,内部对 TabBar 页面自动走 uni.switchTab
|
||||||
|
// (鸿蒙元服务下还会用 reLaunch 兜底,见 common/js/util.js)
|
||||||
|
// H5 端保留 show_home / goHome(虽然模板里不渲染自绘 navbar,但保留方法调用不报错)
|
||||||
|
goHome() {
|
||||||
|
this.$util.redirectTo(this.$util.INDEX_PAGE_URL, {}, 'redirectTo');
|
||||||
|
},
|
||||||
getData() {
|
getData() {
|
||||||
this.$api.sendRequest({
|
this.$api.sendRequest({
|
||||||
url: '/form/api/form/info',
|
url: '/form/api/form/info',
|
||||||
@@ -176,19 +253,15 @@ export default {
|
|||||||
formName = this.formTitle;
|
formName = this.formTitle;
|
||||||
}
|
}
|
||||||
// #endif
|
// #endif
|
||||||
|
// H5 端:系统原生 navbar —— uni.setNavigationBarTitle 改原生标题
|
||||||
|
// #ifdef H5
|
||||||
uni.setNavigationBarTitle({ title: formName });
|
uni.setNavigationBarTitle({ title: formName });
|
||||||
},
|
// #endif
|
||||||
// 返回主页(用 reLaunch 避免返回后还能 navigateBack 回到已提交的表单页)
|
// 非 H5 端:custom 模式下没有原生 navbar,直接写 data.navbarTitle,
|
||||||
goHome() {
|
// 由模板里的 .form-home-navbar__title 显示
|
||||||
this.$util.redirectTo(this.$util.INDEX_PAGE_URL, {}, 'reLaunch');
|
// #ifndef H5
|
||||||
},
|
this.navbarTitle = formName;
|
||||||
// 清空表单数据:通过 diy-form 的 ref 拿到 ns-newform 实例,调用 setFormData 重置
|
// #endif
|
||||||
resetForm() {
|
|
||||||
try {
|
|
||||||
if (this.$refs.form && this.$refs.form.$refs.nsNewform && this.$refs.form.$refs.nsNewform.setFormData) {
|
|
||||||
this.$refs.form.$refs.nsNewform.setFormData();
|
|
||||||
}
|
|
||||||
} catch(e) {}
|
|
||||||
},
|
},
|
||||||
// 由 diy-form 内置提交按钮触发:diy-form handleSubmit 已先做 verify(),
|
// 由 diy-form 内置提交按钮触发:diy-form handleSubmit 已先做 verify(),
|
||||||
// 仅在通过时才通过 :onSubmit 函数 prop 调用本方法(跨 H5 / MP 一致,不走 $emit / $listeners)。
|
// 仅在通过时才通过 :onSubmit 函数 prop 调用本方法(跨 H5 / MP 一致,不走 $emit / $listeners)。
|
||||||
@@ -209,18 +282,15 @@ export default {
|
|||||||
member_id: this.memberInfo ? this.memberInfo.id : 0
|
member_id: this.memberInfo ? this.memberInfo.id : 0
|
||||||
},
|
},
|
||||||
success: res => {
|
success: res => {
|
||||||
if (res.code == 0) {
|
if (res.code == 0) {
|
||||||
this.complete = true
|
this.complete = true
|
||||||
// 提交成功后清空表单
|
uni.showModal({
|
||||||
this.resetForm()
|
title: this.$lang('form.formdata.tip'),
|
||||||
this.isRepeat = false
|
content: this.$lang('form.formdata.submitSuccess'),
|
||||||
uni.showModal({
|
showCancel: false,
|
||||||
title: this.$lang('form.formdata.tip'),
|
success: () => { }
|
||||||
content: this.$lang('form.formdata.submitSuccess'),
|
})
|
||||||
showCancel: false,
|
} else {
|
||||||
success: () => { }
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.isRepeat = false
|
this.isRepeat = false
|
||||||
this.$util.showToast({
|
this.$util.showToast({
|
||||||
title: res.message
|
title: res.message
|
||||||
@@ -237,6 +307,114 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
/*
|
||||||
|
* 自绘 navbar(非 H5 端:APP / 微信小程序 / 华为快应用 / 鸿蒙元服务)
|
||||||
|
* - pages.json 已为这几端开启 navigationStyle:custom,系统不再提供原生 navbar
|
||||||
|
* - 整体高度由 inline style 注入:statusBarHeight + 44px
|
||||||
|
* - 左侧 Home 胶囊 + 中央标题 + 右侧占位,标题过长自动省略
|
||||||
|
*/
|
||||||
|
/* #ifndef H5 */
|
||||||
|
|
||||||
|
/* --- 自绘 navbar 容器 --- */
|
||||||
|
.form-home-navbar {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #ffffff;
|
||||||
|
/* 高度由 inline style 通过 customNavbarStyle 注入:statusBarHeight + 44px */
|
||||||
|
/* padding-top 把内容区下移到状态栏之下,避免被刘海/水滴遮挡 */
|
||||||
|
/* 浮在表单内容之上,避免被 diy-form 等组件覆盖 */
|
||||||
|
z-index: 100;
|
||||||
|
/* 让内容靠 flex 自动落到 padding-top 之下的区域 */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-home-navbar__left {
|
||||||
|
/* 不再用 absolute,跟着 navbar 的 padding-top 下移 */
|
||||||
|
flex: none;
|
||||||
|
/* 宽度自适应:返回按钮 56rpx + Home 胶囊 56rpx + 间距 12rpx + 左 padding 16rpx = 最多 ~140rpx */
|
||||||
|
min-width: 88rpx;
|
||||||
|
max-width: 200rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding-left: 16rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-home-navbar__back {
|
||||||
|
width: 56rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-home-navbar__back-icon {
|
||||||
|
font-size: 48rpx;
|
||||||
|
line-height: 1;
|
||||||
|
color: #000;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-home-navbar__btn--hover {
|
||||||
|
background: rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-home-navbar__title {
|
||||||
|
flex: 1;
|
||||||
|
height: 44px;
|
||||||
|
line-height: 44px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #000;
|
||||||
|
font-weight: 500;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-home-navbar__placeholder {
|
||||||
|
/* 与 __left 对称,保持标题视觉居中 */
|
||||||
|
flex: none;
|
||||||
|
min-width: 88rpx;
|
||||||
|
max-width: 200rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Home 胶囊主体 --- */
|
||||||
|
.form-home-capsule {
|
||||||
|
width: 56rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-home-capsule--hover {
|
||||||
|
background: rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Home 图标 —— 使用项目统一的 iconfont icon-shouye1(unicode \e02d)
|
||||||
|
* 与 pages_goods/detail.vue 的首页按钮共用同一个图标,跨端视觉一致
|
||||||
|
*/
|
||||||
|
.form-home-capsule__icon {
|
||||||
|
font-size: 32rpx;
|
||||||
|
line-height: 1;
|
||||||
|
color: #000;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #endif */
|
||||||
|
</style>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
page {
|
page {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@@ -254,35 +432,6 @@ page {
|
|||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 左上角返回主页按钮
|
|
||||||
.home-btn {
|
|
||||||
position: fixed;
|
|
||||||
left: 30rpx;
|
|
||||||
// #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU || MP-TOUTIAO || MP-QQ || MP-LARK
|
|
||||||
// 小程序端需避开右上角胶囊按钮,下移到状态栏 + 胶囊高度之下
|
|
||||||
top: calc(var(--status-bar-height, 20px) + 10rpx);
|
|
||||||
// #endif
|
|
||||||
// #ifdef H5
|
|
||||||
top: 20rpx;
|
|
||||||
// #endif
|
|
||||||
z-index: 999;
|
|
||||||
padding: 12rpx 28rpx;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
border-radius: 40rpx;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.home-btn-text {
|
|
||||||
color: #ffffff;
|
|
||||||
font-size: 26rpx;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .mescroll-totop {
|
::v-deep .mescroll-totop {
|
||||||
/* #ifdef H5 */
|
/* #ifdef H5 */
|
||||||
right: 27rpx !important;
|
right: 27rpx !important;
|
||||||
|
|||||||
Reference in New Issue
Block a user