实战一: 自定义对象列表页展示(Web端)
效果展示
需求拆解
代码展示
自定义控制器代码:
String sql1 = "select _id, name, tel from AccountObj limit 10 offset 0;"
SelectAttribute att = SelectAttribute.builder()
.needCount(true)
.needInvalid(false)
.build()
List rst1 = Fx.object.select(sql1, att) as List
return rst1[1]
自定义组件代码:
<!--
自定义实现一个对象列表页展示:
1. 实现一个定制化的列表页卡片
2. 需要调用APL函数获取对象数据
3. 点击卡片可以打开查看详情
4. 提供一个按钮,点击后可以新建对象
-->
<template>
<div>
<template v-for="item in dList">
<div class="object-item">
<fx-card :body-style="{ padding: '0px' }">
<img src="https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png" class="image">
<div style="padding: 14px;">
<div> 客户id: {{ item._id}}</div>
<div> 客户名称: {{ item.name }}</div>
<div> 客户电话: {{ item.tel }}</div>
<div class="bottom clearfix" style="text-align: center">
<fx-button type="text" class="button" @click="createObj(item)">新建对象</fx-button>
<fx-button type="text" class="button" @click="objDetail(item)">查看详情</fx-button>
</div>
</div>
</fx-card>
</div>
</template>
</div>
</template>
<script>
export default {
data() {
return {
dList: [
{_id: '', name: '', tel: ''},
]
}
},
created() {
this.fetchData()
},
methods: {
// 获取客户对象列表信息
fetchData() {
FxUI.userDefine.call_controller('GetObjectData__c').then((res) => {
if (res.Result.StatusCode === 0) {
this.dList = res.Value.functionResult.dataList
} else {
alert('调用自定义控制器失败')
console.log(res)
}
})
},
// 新建客户对象
createObj(item) {
console.log('新建客户对象')
FxUI.objectUIAction.addObject('AccountObj', {
showType: 'full'
}).then(res => {
// todo what you want
}).catch(err => {
// handle error
})
},
// 获取客户对象详情
objDetail(item) {
console.log('客户对象详情')
FxUI.objectUIAction.viewObject('AccountObj', item._id)
}
}
}
</script>
<style scoped>
.object-item {
width: 235px;
display: inline-block;
margin: 20px;
}
</style>
踩坑: 关于vue的v-for循环
在div标签中,使用vue的v-for循环,会在每次循环的元素外自动加上一个div标签
下面是问ChatGpt的结果:
实战二: 自定义实现一个对象表单字段(Web端)
效果展示
需求拆解
代码展示
数据传递流向:(下图中字段代表组件)
第三方网页必加的代码:
<template>
</template>
<script>
export default {
name: "ThirdApp",
methods: {
// 第三方网页需要调用的方法
closePage() {
console.log('closePage')
window.parent.postMessage(JSON.stringify({
'name': 'close'
}), '*')
},
sendMessage(data) {
window.parent.postMessage(JSON.stringify({
'name': 'returnResult',
'params': data
}), '*')
},
onItemClick(data) {
this.sendMessage(data)
this.closePage()
}
}
}
</script>
<style scoped>
</style>
测试用第三方网页完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>第三方WebPage</title>
<style>
body{
margin: 0;
padding: 0;
width: 100%;
height: 100vh;
overflow: hidden;
}
p{
margin: 0;
padding: 0;
}
#app {
height: 100%;
}
.container{
padding-bottom: 40px;
height: 100%;
overflow: scroll;
box-sizing: border-box;
}
.item {
height: 100px;
box-sizing: border-box;
width: 100%;
align-items: center;
border-bottom: 1px solid #dee1e8;
padding: 10px 12px;
font-size: 12px;
color: #212b36;
}
.bottom{
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 40px;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
border-top: 1px solid #DEE1E8;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<div v-for="(item, index) in list" class="item" @click="onItemClick(item)" >
<p>公司名称:{{item.name}}</p>
<p>工商注册号:{{item.regNumber}}</p>
<p>法人代表:{{item.legalPerson}}</p>
<p>注册资金:{{item.registeredCapital}}</p>
</div>
</div>
<div class="bottom">
<button @click="closePage">关闭</button>
</div>
</div>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
<script src="https://a9.fspage.com/FSR/uipaas/test/vue2.js"></script>
<script src="https://cdn.bootcss.com/vConsole/3.2.0/vconsole.min.js"></script>
<script src="https://www.fxiaoke.com/open/jsapi/2.3.4/fsapi.min.js"></script>
<script>
var vConsole = new VConsole();
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
list: [
{
"name":"北京纷扬科技有限责任公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"罗旭",
"registeredCapital":"20000万美元",
"regNumber":"110000450231294"
},
{
"name":"广州纷扬信息科技有限公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"李智钒",
"registeredCapital":"100万人民币",
"regNumber":"440106001246204"
},
{
"name":"北京纷扬科技有限责任公司上海分公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"罗旭",
"registeredCapital":"-",
"regNumber":""
},
{
"name":"河南云纷扬网络科技有限公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"谷远",
"registeredCapital":"100万",
"regNumber":"410199000306770"
},
{
"name":"昆明扬纷科技有限公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"李天龙",
"registeredCapital":"10万人民币",
"regNumber":"530100000467573"
},
{
"name":"扬州纷信网络科技有限公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"包国余",
"registeredCapital":"100万人民币",
"regNumber":"321027000439418"
},
{
"name":"纷扬(上海)设计有限公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"丁佳",
"registeredCapital":"100万人民币",
"regNumber":""
},
{
"name":"纷扬(上海)网络科技有限公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"徐海东",
"registeredCapital":"500万人民币",
"regNumber":"310115003187023"
}
]
},
methods: {
closePage() {
console.log('closePage')
window.parent.postMessage(JSON.stringify({
"name": "close"
}), '*');
},
sendMessage(data) {
window.parent.postMessage(JSON.stringify({
"name":"returnResult",
"params": data
}), '*');
},
onItemClick(data) {
this.sendMessage(data)
this.closePage()
}
}
});
</script>
</body>
</html>
新建/编辑页Web端插件:
import InputOpenThirdAppOptions from "/src/components/InputOpenThirdApp.vue";
const InputOpenThirdApp = Vue.extend(InputOpenThirdAppOptions)
export default {
entry(context, field) {
return field.api_name === 'name'
},
render(context, field, value) {
const vm = new InputOpenThirdApp()
vm.$mount()
vm.$on('change', (val) => {
context.setData(field.api_name, val)
})
return vm.$el
}
}
自定义组件:
<template>
<div>
<fx-input placeholder="请输入内容" v-model="input" class="input-with-select">
<fx-button slot="append" icon="el-icon-search" @click="dShow=true"></fx-button>
</fx-input>
<fx-dialog :visible.sync="dShow" @close="dShow=false">
<iframe src="https://a9.fspage.com/FSR/uipaas/test/thirdWebPage.html?v=4"></iframe>
</fx-dialog>
</div>
</template>
<script>
export default {
name: "InputOpenThirdApp",
data() {
return {
input: '',
dShow: false
}
},
created() {
// 现在纷享不能使用window,作了约束,得用top
top.addEventListener('message', (e) => {
console.log(e.data)
if (e.origin === 'https://a9.fspage.com') { // 可能有多个消息源
const data = JSON.parse(e.data)
if(data.name === 'close') {
this.dShow = false
} else if(data.name === 'returnResult') {
this.input = data.params.name
this.dShow = false
this.$emit('change', data.params.name)
}
}
})
},
methods: {
}
}
</script>
<!-- 这里不能加scoped,因为input是fx-input组件内部的一个属性-->
<style>
.input-with-select {
input {
height: auto !important;
}
}
</style>
<!-- 这里必须加scoped, 因为新添加的标签在fx-dialog组件里面-->
<style scoped lang="less">
iframe {
width: 100%;
height: 500px;
}
</style>
实战三: 实现一个页面自定义菜单组件
需求
需求分析
效果展示
代码展示
目录结构:
<!--components/menu/menu.wxml-->
<view class="menu-group">
<view wx:for="{{ dList }}" wx:for-item="menuData">
<view class="menu" bindtap="openPage" data-menu="{{menuData}}">
<image src="{{ menuData.icon }}" mode=""/>
<text>{{ menuData.name }}</text>
</view>
</view>
</view>
/* components/menu/menu.wxss */
.menu-group {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
padding: 20px;
background-color: orange;
}
.menu {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
padding-right: 10px;
}
.menu image {
width: 40px;
height: 40px;
border-radius: 40px;
margin-top: 15px;
margin-bottom: 10px;
}
.menu text {
font-size: 12px;
color: ghostwhite;
}
// components/menu/menu.js
import fsApi from "fs-hera-api"
Component({
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
dList: [
{
name: 'qq',
icon: 'https://n.sinaimg.cn/sinakd20220708s/579/w690h689/20220708/093c-ebb3af959f02d6334803c3015fe6492b.jpg',
link: "https://im.qq.com/index/"
},
{
name: '自定义H5应用',
icon: 'https://n.sinaimg.cn/sinakd20220708s/579/w690h689/20220708/093c-ebb3af959f02d6334803c3015fe6492b.jpg',
link: "https://fxiaoke.com"
},
{
name: '暂时未定义',
icon: 'https://n.sinaimg.cn/sinakd20220708s/579/w690h689/20220708/093c-ebb3af959f02d6334803c3015fe6492b.jpg',
link: 'https://fxiaoke.com'
}
]
},
/**
* 组件的方法列表
*/
methods: {
openPage(e) {
console.log
let data = e.currentTarget.dataset['menu']
console.log(data)
fsApi.page.utilOpen({
name: data.link
})
}
}
})
config.json文件:
去除默认样式:
实战四: 自定义插件实现第三方h5列表数据回填至表单
需求
效果展示
代码展示
第三方网页必加的代码:
<template>
</template>
<script>
export default {
name: "ThirdApp",
methods: {
closePage() {
console.log('closePage')
FSOpen.webview.close()
},
sendMessage(data) {
console.log('sendMessage')
FSOpen.util.sendNotification({
"name":"returnResult",
"params": data
})
},
onItemClick(data) {
this.sendMessage(data)
this.closePage()
}
}
}
</script>
<style scoped>
</style>
第三方网页完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>第三方h5Page</title>
<style>
body{
margin: 0;
padding: 0;
width: 100%;
height: 100vh;
overflow: hidden;
}
p{
margin: 0;
padding: 0;
}
#app {
height: 100%;
}
.container{
padding-bottom: 40px;
height: 100%;
overflow: scroll;
box-sizing: border-box;
}
.item {
height: 100px;
box-sizing: border-box;
width: 100%;
align-items: center;
border-bottom: 1px solid #dee1e8;
padding: 10px 12px;
font-size: 12px;
color: #212b36;
}
.bottom{
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 40px;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
border-top: 1px solid #DEE1E8;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<div v-for="(item, index) in list" class="item" @click="onItemClick(item)" >
<p>公司名称:{{item.name}}</p>
<p>工商注册号:{{item.regNumber}}</p>
<p>法人代表:{{item.legalPerson}}</p>
<p>注册资金:{{item.registeredCapital}}</p>
</div>
</div>
<div class="bottom">
<button @click="closePage">关闭</button>
</div>
</div>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
<script src="https://a9.fspage.com/FSR/uipaas/test/vue2.js"></script>
<script src="https://cdn.bootcss.com/vConsole/3.2.0/vconsole.min.js"></script>
<script src="https://www.fxiaoke.com/open/jsapi/2.3.4/fsapi.min.js"></script>
<script>
var vConsole = new VConsole();
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
list: [
{
"name":"北京纷扬科技有限责任公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"罗旭",
"registeredCapital":"20000万美元",
"regNumber":"110000450231294"
},
{
"name":"广州纷扬信息科技有限公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"李智钒",
"registeredCapital":"100万人民币",
"regNumber":"440106001246204"
},
{
"name":"北京纷扬科技有限责任公司上海分公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"罗旭",
"registeredCapital":"-",
"regNumber":""
},
{
"name":"河南云纷扬网络科技有限公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"谷远",
"registeredCapital":"100万",
"regNumber":"410199000306770"
},
{
"name":"昆明扬纷科技有限公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"李天龙",
"registeredCapital":"10万人民币",
"regNumber":"530100000467573"
},
{
"name":"扬州纷信网络科技有限公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"包国余",
"registeredCapital":"100万人民币",
"regNumber":"321027000439418"
},
{
"name":"纷扬(上海)设计有限公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"丁佳",
"registeredCapital":"100万人民币",
"regNumber":""
},
{
"name":"纷扬(上海)网络科技有限公司",
"tel":"123",
"email":"123@163.com",
"legalPerson":"徐海东",
"registeredCapital":"500万人民币",
"regNumber":"310115003187023"
}
]
},
methods: {
closePage() {
console.log('closePage')
FSOpen.webview.close()
},
sendMessage(data) {
console.log('sendMessage')
FSOpen.util.sendNotification({
"name":"returnResult",
"params": data
})
},
onItemClick(data) {
this.sendMessage(data)
this.closePage()
}
}
});
</script>
</body>
</html>
插件:
export default function (context) {
return {
customFieldCom({field}) {
let comInfo = null;
if (field && field.api_name === 'field_3Xlbm__c') {
console.log("test plugin: customFieldCom: " + field.api_name)
comInfo = {
name: "cmpt1",
prop: {test: 1}
}
}
return Promise.resolve(comInfo)//异步return
//return comInfo //或者直接同步return
}
}
组件:
<view class="container">
<view>
<view class="i-cell">
<view class="i-cell-hd">
<view class="text">
插件修改字段名
</view>
</view>
<view class="i-cell-bd">
<view class="i-cell-input">
<button size="mini" type="primary" bindtap="openWebView"> 打开第三方h5</button>
</view>
</view>
</view>
</view>
</view>
.i-cell {
display: flex;
height: 40px;
box-sizing: border-box;
align-items: center;
}
.i-cell-hd {
padding: 10px;
}
import fieldbehavior from "../../fieldbehavior";
import fsApi from "fs-hera-api"
Component({
behaviors:[fieldbehavior],
methods:{
openWebView() {
fsApi.jsapi.webviewOpen({
url: `https://a9.fspage.com/FSR/uipaas/test/thirdH5Page.html?v=${+new Date()}`,
onSuccess: (data) => {
this.data.context.setData({
"field_CBcdg__c": data.tel,
"field_oi1K9__c": data.name,
"field_e8x8y__c": data.regNumber,
"field_47dj2__c": data.registeredCapital
})
}
})
},
_testMethod(){
let {context, field}=this.data;
let random = Math.round(Math.random()*1000)+''
context.setData({
[field.api_name]:random
})
}
},
lifetimes:{
attached() {
if(wx.getSystemInfoSync().brand=="devtools"){
const mockUtil = require("../../mockUtils")
mockUtil.initFieldCom(this, {
field: {api_name:"test__c", type:'text', label:"测试字段label"},
is_required:true,
is_readonly: false,
value: null
})
}
console.group("自定义字段组件 attached")
console.log(this.data)
console.log(this.data.field)
console.log(this.data.context)
console.groupEnd()
}
}
});
常用技巧与案例解析
1. 租户配置存储
2. 系统嵌入
3. 跨系统数据通讯
实战二与实战四中都涉及了跨系统数据通讯,一个是web与web(第三方)跨系统通讯,另一个是小程序与web(第三方)跨系统通讯
4. 第三方app集成
5. 依赖第三方库
方式1:
方式2: