Compare commits

...

10 Commits

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

@ -0,0 +1,26 @@
# 三维识别与重建云服务
## proto更新
### 依赖安装
```
// ARModel 下
tnpm install --save-dev @tencent/cloud-functions-tools@latest
```
### 基于 proto 文件生成脚本逻辑
```
// ARModel 下
npm run svrkit
```
## 服务更新
```
1. 安装 ARModel 下本地 npm 依赖
2. 云函数环境切为 test环境
3. ARModel 右键上传所有文件(由于有@tencent的子包
```

@ -0,0 +1,23 @@
#!/usr/bin/env node
const path = require('path')
const yargs = require('yargs')
yargs
.usage('Usage: $0 -c [config] -o [output]')
.example('$0 -c ./svrkit.config.js -o ./svrkit-utils.js')
.alias('c', 'config')
.describe('c', 'svrkit config js file path')
.alias('o', 'output')
.describe('o', 'svrkit-utils output file path, defaults to svrkit-utils.js under the same folder of svrkit config file')
.describe('--keep-case', 'keeps field casing instead of converting to camcel case')
.demandOption(['c'])
.help('h')
.alias('h', 'help')
.argv
const cli = require(path.join(__dirname, '../cli/svrkit-utils.js'))
const ret = cli.main(process.argv.slice(2))
if (typeof ret === 'number') {
process.exit(ret)
}

@ -0,0 +1,114 @@
function generate(options) {
if (!options) {
throw new Error('options must be provided')
}
const { serviceName, funcName, data } = options
const serviceConfig = config.find(c => c.serviceName === serviceName)
if (!serviceConfig) {
throw new Error('service not found')
}
if (!serviceConfig.functions[funcName]) {
throw new Error('function not found')
}
const reqProtoName = serviceConfig.functions[funcName].req
const reqProto = proto[reqProtoName]
if (!reqProto) {
throw new Error('request proto not found')
}
const resProtoName = serviceConfig.functions[funcName].res
const resProto = resProtoName && proto[resProtoName]
const reqProtoVerifyErr = reqProto.verify(data)
if (reqProtoVerifyErr) {
throw new Error(`verify proto data error: ${reqProtoVerifyErr}`)
}
const reqProtoJSON = protoJSON.nested[reqProtoName]
if (reqProtoJSON && reqProtoJSON.fields) {
if (Object.prototype.toString.call(data).slice(8, -1) === 'Object') {
for (const key in data) {
if (!reqProtoJSON.fields[key]) {
throw new Error(`'${key}' doesn't exist in '${reqProtoName}' proto, valid keys are ${Object.keys(reqProtoJSON.fields)}`)
}
}
} else {
throw new Error('data must be object')
}
}
return {
data: {
serviceName,
funcName,
magic: serviceConfig.magic,
cmdid: serviceConfig.functions[funcName].cmdid,
existResp: Boolean(resProto),
reqBodyBuffer: reqProto.encode(data).finish(),
},
reqProto,
resProto,
decode: buf => resProto && resProto.decode(buf)
}
}
function generateV2(options) {
if (!options) {
throw new Error('options must be provided')
}
const { apiName, data } = options
const apiConfig = config.find(c => c.apiName === apiName)
const reqProtoName = apiConfig.req
const reqProto = proto[reqProtoName]
if (!reqProto) {
throw new Error('request proto not found')
}
const resProtoName = apiConfig.res
const resProto = proto[resProtoName]
const reqProtoVerifyErr = reqProto.verify(data)
if (reqProtoVerifyErr) {
throw new Error(`verify proto data error: ${reqProtoVerifyErr}`)
}
const reqProtoJSON = protoJSON.nested[reqProtoName]
if (reqProtoJSON && reqProtoJSON.fields) {
if (Object.prototype.toString.call(data).slice(8, -1) === 'Object') {
for (const key in data) {
if (!reqProtoJSON.fields[key]) {
throw new Error(`'${key}' doesn't exist in '${reqProtoName}' proto, valid keys are ${Object.keys(reqProtoJSON.fields)}`)
}
}
} else {
throw new Error('data must be object')
}
}
return {
data: {
apiName,
reqBodyBuffer: reqProto.encode(data).finish(),
},
reqProto,
resProto,
decode: buf => resProto && resProto.decode(buf)
}
}
module.exports = {
generate,
generateV2,
}

@ -0,0 +1,98 @@
const fs = require('fs')
const path = require('path')
const yargs = require('yargs')
const chalk = require('chalk')
const debug = require('debug')('cli')
const pbjs = require("protobufjs/cli/pbjs")
const log = (...msg) => {
console.log(chalk.blue('svrkit-utils'), ...msg)
}
function main() {
debug('process.cwd', process.cwd())
debug('yargs.argv', yargs.argv)
if (yargs.argv.config) {
const configPath = path.resolve(process.cwd(), yargs.argv.config)
const config = require(configPath)
const protos = config.map(c => path.resolve(path.dirname(configPath), c.proto))
if (yargs.argv.output) {
if (!yargs.argv.output.endsWith('.js')) {
throw new Error('output file name must ends with .js')
}
}
const outputDest = yargs.argv.output || path.resolve(path.dirname(configPath), 'svrkit-utils.js')
const outputFileName = path.basename(outputDest)
const outputFilePath = path.resolve(process.cwd(), outputDest)
debug('outputDest', outputDest)
debug('outputFilePath', outputFilePath)
const staticModuleFileName = `${outputFileName.slice(0, -3)}.static.js`
const staticModuleFilePath = path.resolve(path.dirname(outputFilePath), staticModuleFileName)
const staticJsonFileName = `${outputFileName.slice(0, -3)}.static.json`
const staticJsonFilePath = path.resolve(path.dirname(outputFilePath), staticJsonFileName)
log('generating static module')
const pbjsArgs = ['-t', 'static-module', '-w', 'commonjs', '-l', 'eslint-disable', '-o', staticModuleFilePath, ...protos]
if (yargs.argv.keepCase) {
pbjsArgs.unshift('--keep-case')
}
pbjs.main(pbjsArgs, (err, out) => {
if (err) {
throw err
}
let staticModuleContent = fs.readFileSync(staticModuleFilePath, 'utf8')
fs.writeFileSync(staticModuleFilePath, `// #lizard forgives
${staticModuleContent}`, 'utf8')
log('static module generated')
log('generating json descriptors')
pbjs.main(['-t', 'json', '-o', staticJsonFilePath, ...protos], (err, out) => {
if (err) {
throw err
}
log('json descriptors generated')
try {
const protoUtils = fs.readFileSync(path.join(__dirname, './svrkit-utils-template.js'), 'utf8')
let svrkitConfigRelativePath = path.relative(path.dirname(outputDest), configPath)
if (!svrkitConfigRelativePath.startsWith('.')) {
svrkitConfigRelativePath = `./${svrkitConfigRelativePath}`
}
const output = `
const config = require('${svrkitConfigRelativePath}')
const proto = require('./${staticModuleFileName}')
const protoJSON = require('./${staticJsonFileName}')
${protoUtils}
`
fs.writeFileSync(outputFilePath, output, 'utf8')
log(`${outputFileName} generated`)
} catch (err) {
throw err
}
})
})
} else {
throw new Error('config file must be provided')
}
}
module.exports = {
main,
}

@ -0,0 +1,20 @@
{
"name": "@tencent/cloud-functions-tools",
"version": "1.5.1",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "alankldeng",
"license": "ISC",
"bin": {
"svrkit-utils": "bin/svrkit-utils"
},
"dependencies": {
"chalk": "^2.4.1",
"debug": "^4.1.0",
"protobufjs": "^6.8.8",
"yargs": "^12.0.5"
}
}

@ -0,0 +1,38 @@
const config = require('./svrkit.config.js')
const proto = require('./bundle.js')
function generate(options) {
if (!options) {
throw new Error('options must be provided')
}
const { serviceName, funcName, data } = options
const serviceConfig = config.find(c => c.serviceName === serviceName)
if (!serviceConfig) {
throw new Error('service not found')
}
if (!serviceConfig.functions[funcName]) {
throw new Error('function not found')
}
const reqProto = proto[serviceConfig.functions[funcName].req]
const resProto = proto[serviceConfig.functions[funcName].res]
return {
data: {
serviceName,
funcName,
magic: serviceConfig.magic,
cmdid: serviceConfig.functions[funcName].cmdid,
existResp: serviceConfig.functions[funcName].existResp,
reqBodyBuffer: reqProto.encode(data),
},
decode: buf => resProto.decode(resProto)
}
}
module.exports = {
generate,
}

@ -0,0 +1,9 @@
message ApiDemoReq
{
optional string str = 2;
}
message ApiDemoResp
{
optional string str = 2;
}

@ -0,0 +1,20 @@
message GetWeAppMemberByUserReq
{
optional uint32 useruin = 1;
optional uint32 type = 2; //1: 2: 3: 4:
optional uint32 status = 3; //SAFECENTER_WEAPP_STATUS_XXX
}
message WeAppMemberInfo
{
optional uint32 type = 1;
optional uint32 weappuin = 2;
optional uint32 useruin = 3;
optional uint32 status = 4;
optional uint32 createtime = 5;
optional uint32 updatetime = 6;
optional string ticket = 7;
}
message WeAppMemberInfoList
{
repeated WeAppMemberInfo infos = 1;
}

@ -0,0 +1,11 @@
message GenWxaCloudTmpCodeReq
{
optional uint32 CloudPlatform = 1;
optional uint32 AppUin = 2;
optional uint32 UserUin = 3;
}
message GenWxaCloudTmpCodeResp
{
optional string TmpCode = 1;
}

@ -0,0 +1,119 @@
const config = require('./svrkit.config.js')
const proto = require('./svrkit-utils.static.js')
const protoJSON = require('./svrkit-utils.static.json')
function generate(options) {
if (!options) {
throw new Error('options must be provided')
}
const { serviceName, funcName, data } = options
const serviceConfig = config.find(c => c.serviceName === serviceName)
if (!serviceConfig) {
throw new Error('service not found')
}
if (!serviceConfig.functions[funcName]) {
throw new Error('function not found')
}
const reqProtoName = serviceConfig.functions[funcName].req
const reqProto = proto[reqProtoName]
if (!reqProto) {
throw new Error('request proto not found')
}
const resProtoName = serviceConfig.functions[funcName].res
const resProto = resProtoName && proto[resProtoName]
const reqProtoVerifyErr = reqProto.verify(data)
if (reqProtoVerifyErr) {
throw new Error(`verify proto data error: ${reqProtoVerifyErr}`)
}
const reqProtoJSON = protoJSON.nested[reqProtoName]
if (reqProtoJSON && reqProtoJSON.fields) {
if (Object.prototype.toString.call(data).slice(8, -1) === 'Object') {
for (const key in data) {
if (!reqProtoJSON.fields[key]) {
throw new Error(`'${key}' doesn't exist in '${reqProtoName}' proto, valid keys are ${Object.keys(reqProtoJSON.fields)}`)
}
}
} else {
throw new Error('data must be object')
}
}
return {
data: {
serviceName,
funcName,
magic: serviceConfig.magic,
cmdid: serviceConfig.functions[funcName].cmdid,
existResp: Boolean(resProto),
reqBodyBuffer: reqProto.encode(data).finish(),
},
reqProto,
resProto,
decode: buf => resProto && resProto.decode(buf)
}
}
function generateV2(options) {
if (!options) {
throw new Error('options must be provided')
}
const { apiName, data } = options
const apiConfig = config.find(c => c.apiName === apiName)
const reqProtoName = apiConfig.req
const reqProto = proto[reqProtoName]
if (!reqProto) {
throw new Error('request proto not found')
}
const resProtoName = apiConfig.res
const resProto = proto[resProtoName]
const reqProtoVerifyErr = reqProto.verify(data)
if (reqProtoVerifyErr) {
throw new Error(`verify proto data error: ${reqProtoVerifyErr}`)
}
const reqProtoJSON = protoJSON.nested[reqProtoName]
if (reqProtoJSON && reqProtoJSON.fields) {
if (Object.prototype.toString.call(data).slice(8, -1) === 'Object') {
for (const key in data) {
if (!reqProtoJSON.fields[key]) {
throw new Error(`'${key}' doesn't exist in '${reqProtoName}' proto, valid keys are ${Object.keys(reqProtoJSON.fields)}`)
}
}
} else {
throw new Error('data must be object')
}
}
return {
data: {
apiName,
reqBodyBuffer: reqProto.encode(data).finish(),
},
reqProto,
resProto,
decode: buf => resProto && resProto.decode(buf)
}
}
module.exports = {
generate,
generateV2,
}

@ -0,0 +1,101 @@
{
"nested": {
"ApiDemoReq": {
"fields": {
"str": {
"type": "string",
"id": 2
}
}
},
"ApiDemoResp": {
"fields": {
"str": {
"type": "string",
"id": 2
}
}
},
"GenWxaCloudTmpCodeReq": {
"fields": {
"CloudPlatform": {
"type": "uint32",
"id": 1
},
"AppUin": {
"type": "uint32",
"id": 2
},
"UserUin": {
"type": "uint32",
"id": 3
}
}
},
"GenWxaCloudTmpCodeResp": {
"fields": {
"TmpCode": {
"type": "string",
"id": 1
}
}
},
"GetWeAppMemberByUserReq": {
"fields": {
"useruin": {
"type": "uint32",
"id": 1
},
"type": {
"type": "uint32",
"id": 2
},
"status": {
"type": "uint32",
"id": 3
}
}
},
"WeAppMemberInfo": {
"fields": {
"type": {
"type": "uint32",
"id": 1
},
"weappuin": {
"type": "uint32",
"id": 2
},
"useruin": {
"type": "uint32",
"id": 3
},
"status": {
"type": "uint32",
"id": 4
},
"createtime": {
"type": "uint32",
"id": 5
},
"updatetime": {
"type": "uint32",
"id": 6
},
"ticket": {
"type": "string",
"id": 7
}
}
},
"WeAppMemberInfoList": {
"fields": {
"infos": {
"rule": "repeated",
"type": "WeAppMemberInfo",
"id": 1
}
}
}
}
}

@ -0,0 +1,32 @@
module.exports = [
{
proto: './proto/demo.proto',
apiName: 'demo',
req: 'ApiDemoReq',
res: 'ApiDemoResp'
},
{
proto: './proto/mmbizwxatmpcode.proto',
serviceName: 'MMBizWxaCloud',
magic: 13299,
functions: {
GenWxaCloudTmpCode: {
cmdid: 3,
req: 'GenWxaCloudTmpCodeReq',
res: 'GenWxaCloudTmpCodeResp',
},
}
},
{
proto: './proto/mmbizsafecenter.proto',
serviceName: 'mmbizsafecenter',
magic: 12085,
functions: {
GetWeAppMemberByUser: {
cmdid: 73,
req: 'GetWeAppMemberByUserReq',
res: 'WeAppMemberInfoList',
},
}
},
]

@ -0,0 +1,62 @@
// 云函数入口文件
const cloud = require('wx-server-sdk')
const wxgService = require('./wx-server-sdk-wxg-service')
const svrkitUtils = require('./svrkit-utils.js')
cloud.registerService(wxgService)
cloud.init()
// 云函数入口函数
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
const bizuin = wxContext.APPUIN
switch (event.type) {
case "GenerateARModel":
return await cloud.callWXSvrkit({
pbInstance: svrkitUtils.generate({
serviceName: "Mmbizwxaintpar",
funcName: "GenerateARModel",
data: {
bizuin: bizuin,
name: event.name,
url: event.url,
algoType: event.algoType,
getmesh: event.getMesh,
gettexture: event.getTexture
},
}),
timeout: 30000,
});
case "GetARModel":
return await cloud.callWXSvrkit({
pbInstance: svrkitUtils.generate({
serviceName: "Mmbizwxaintpar",
funcName: "GetARModel",
data: {
bizuin: bizuin,
cosid: event.cosid,
modelType: event.modelType,
needData: event.needData,
useIntranet: event.useIntranet,
expireTime: event.expireTime
},
}),
timeout: 30000,
});
// GetARModelList 废弃,完全依赖本地缓存
// case "GetARModelList":
// return await cloud.callWXSvrkit({
// pbInstance: svrkitUtils.generate({
// serviceName: "Mmbizwxaintpar",
// funcName: "GetARModelList",
// data: {
// bizuin: bizuin,
// modelStatus: event.modelStatus,
// algoType: event.algoType
// },
// }),
// timeout: 30000,
// });
}
}

@ -0,0 +1,18 @@
{
"name": "ARDemo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"svrkit": "svrkit-utils --config ./svrkit.config.js --output ./svrkit-utils.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"wx-server-sdk": "^2.6.3"
},
"devDependencies": {
"@tencent/cloud-functions-tools": "^1.7.0"
}
}

@ -0,0 +1,115 @@
enum enARModelStatus
{
ARModel_Status_Default = 0;
ARModel_Status_Init = 1;
ARModel_Status_Sparse_Finished = 2;
ARModel_Status_3d_Finished = 3;
ARModel_Status_Object_Finished = 4;
ARModel_Status_Marker_Finished = 5;
ARModel_Status_Fail = 100;
}
enum enARAlgorithmType
{
Algorithm_Type_3D_Object = 1;
Algorithm_Type_3D_Marker = 2;
}
enum enARModelType
{
ARModel_Type_Sparse = 1;
ARModel_Type_3D = 2;
ARModel_Type_Marker = 3;
}
message ModelCos
{
message ModelCosId
{
optional enARModelType model_type = 1;
optional string model_cosid = 2;
optional string errmsg = 3;
}
repeated ModelCosId model_list = 1;
}
message ARModel
{
//option(mmbizintpkv.KvTableID) = 493;
option(mmbizintpkv.KvTableTestID) = 916;
optional string cosid = 1; // cosid
optional uint32 bizuin = 2;
optional string name = 3; //
optional uint32 upload_time = 4;
optional enARModelStatus model_status = 5;
optional enARAlgorithmType algo_type = 6;
optional ModelCos model_cos = 7;
}
message GetARModelListReq
{
optional uint32 bizuin = 1;
optional uint32 model_status = 2; // enARModelStatus
optional uint32 start_time = 3;
optional uint32 end_time = 4;
optional uint32 offset = 5;
optional uint32 limit = 6;
optional uint32 algo_type = 7; // enARAlgorithmType
}
message GetARModelListResp
{
repeated ARModel model_list = 1;
}
message GenerateARModelReq
{
optional uint32 bizuin = 1;
optional string name = 2;
optional bytes buffer = 3;
optional string url = 4;
optional enARAlgorithmType algo_type = 5;
optional uint32 lod = 6[default=0]; // , 0, 1,2,3
optional bool getmesh = 7[default=false];
optional bool gettexture = 8[default=false];
}
message GenerateARModelResp
{
optional string url = 1;
optional string host = 2;
optional string cosid = 3;
optional uint32 lod = 4[default=0];
optional bool getmesh = 5[default=false];
optional bool gettexture = 6[default=false];
}
message ARModelData
{
optional bytes mesh_model = 1; // ()
optional bytes texture_model = 2; // png
optional bytes preview = 3;
optional bytes mesh_blob = 4; // obj, getmesh = true
optional bytes texture_blob = 5; // , gettexture = true
}
message GetARModelReq
{
optional uint32 bizuin = 1;
optional string cosid = 2;
optional uint32 model_type = 3; // 1: 2:3d
optional uint32 need_data = 4[default=1]; // 0: 1:
optional uint32 use_intranet = 5[default=0]; // need_data0 0: 1:
optional uint32 expire_time = 6; // url,5,
}
message GetARModelResp
{
optional ARModelData model_data = 1;
optional string url = 2;
optional string host = 3;
optional string errMsg = 4;
optional uint32 expire_time = 5;
optional uint32 status = 6; // 0 1 2
}

@ -0,0 +1,136 @@
const config = require('./svrkit.config.js')
const proto = require('./svrkit-utils.static.js')
const protoJSON = require('./svrkit-utils.static.json')
function getProto(proto, serviceName, protoName) {
if (proto[protoName]) {
return proto[protoName];
}
if (proto[serviceName] && proto[serviceName][protoName]) {
return proto[serviceName][protoName];
}
/** 处理 mmpayolcirclemodel.QueryActivityReq 的形式*/
const [realServiceName, realProtoName] = protoName.split('.')
if (proto[realServiceName]) {
return proto[realServiceName][realProtoName]
}
return undefined;
}
function generate(options) {
if (!options) {
throw new Error('options must be provided')
}
const { serviceName, funcName, data } = options
const serviceConfig = config.find(c => c.serviceName === serviceName)
if (!serviceConfig) {
throw new Error('service not found')
}
if (!serviceConfig.functions[funcName]) {
throw new Error('function not found')
}
const reqProtoName = serviceConfig.functions[funcName].req
const reqProto = getProto(proto, serviceName, reqProtoName);
if (!reqProto) {
throw new Error('request proto not found')
}
const resProtoName = serviceConfig.functions[funcName].res
const resProto = resProtoName && getProto(proto, serviceName, resProtoName);
const reqProtoVerifyErr = reqProto.verify(data)
if (reqProtoVerifyErr) {
throw new Error(`verify proto data error: ${reqProtoVerifyErr}`)
}
const reqProtoJSON = protoJSON.nested[reqProtoName]
if (reqProtoJSON && reqProtoJSON.fields) {
if (Object.prototype.toString.call(data).slice(8, -1) === 'Object') {
for (const key in data) {
if (!reqProtoJSON.fields[key]) {
throw new Error(`'${key}' doesn't exist in '${reqProtoName}' proto, valid keys are ${Object.keys(reqProtoJSON.fields)}`)
}
}
} else {
throw new Error('data must be object')
}
}
return {
data: {
serviceName,
funcName,
magic: serviceConfig.magic,
cmdid: serviceConfig.functions[funcName].cmdid,
existResp: Boolean(resProto),
reqBodyBuffer: reqProto.encode(data).finish(),
},
reqProto,
resProto,
decode: buf => resProto && resProto.decode(buf)
}
}
function generateV2(options) {
if (!options) {
throw new Error('options must be provided')
}
const { apiName, data } = options
const apiConfig = config.find(c => c.apiName === apiName)
const reqProtoName = apiConfig.req
const reqProto = proto[reqProtoName]
if (!reqProto) {
throw new Error('request proto not found')
}
const resProtoName = apiConfig.res
const resProto = proto[resProtoName]
const reqProtoVerifyErr = reqProto.verify(data)
if (reqProtoVerifyErr) {
throw new Error(`verify proto data error: ${reqProtoVerifyErr}`)
}
const reqProtoJSON = protoJSON.nested[reqProtoName]
if (reqProtoJSON && reqProtoJSON.fields) {
if (Object.prototype.toString.call(data).slice(8, -1) === 'Object') {
for (const key in data) {
if (!reqProtoJSON.fields[key]) {
throw new Error(`'${key}' doesn't exist in '${reqProtoName}' proto, valid keys are ${Object.keys(reqProtoJSON.fields)}`)
}
}
} else {
throw new Error('data must be object')
}
}
return {
data: {
apiName,
reqBodyBuffer: reqProto.encode(data).finish(),
},
reqProto,
resProto,
decode: buf => resProto && resProto.decode(buf)
}
}
module.exports = {
generate,
generateV2,
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,299 @@
{
"nested": {
"enARModelStatus": {
"values": {
"ARModel_Status_Default": 0,
"ARModel_Status_Init": 1,
"ARModel_Status_Sparse_Finished": 2,
"ARModel_Status_3d_Finished": 3,
"ARModel_Status_Object_Finished": 4,
"ARModel_Status_Marker_Finished": 5,
"ARModel_Status_Fail": 100
}
},
"enARAlgorithmType": {
"values": {
"Algorithm_Type_3D_Object": 1,
"Algorithm_Type_3D_Marker": 2
}
},
"enARModelType": {
"values": {
"ARModel_Type_Sparse": 1,
"ARModel_Type_3D": 2,
"ARModel_Type_Marker": 3
}
},
"ModelCos": {
"fields": {
"modelList": {
"rule": "repeated",
"type": "ModelCosId",
"id": 1
}
},
"nested": {
"ModelCosId": {
"fields": {
"modelType": {
"type": "enARModelType",
"id": 1
},
"modelCosid": {
"type": "string",
"id": 2
},
"errmsg": {
"type": "string",
"id": 3
}
}
}
}
},
"ARModel": {
"options": {
"(mmbizintpkv.KvTableTestID)": 916
},
"fields": {
"cosid": {
"type": "string",
"id": 1
},
"bizuin": {
"type": "uint32",
"id": 2
},
"name": {
"type": "string",
"id": 3
},
"uploadTime": {
"type": "uint32",
"id": 4
},
"modelStatus": {
"type": "enARModelStatus",
"id": 5
},
"algoType": {
"type": "enARAlgorithmType",
"id": 6
},
"modelCos": {
"type": "ModelCos",
"id": 7
}
}
},
"GetARModelListReq": {
"fields": {
"bizuin": {
"type": "uint32",
"id": 1
},
"modelStatus": {
"type": "uint32",
"id": 2
},
"startTime": {
"type": "uint32",
"id": 3
},
"endTime": {
"type": "uint32",
"id": 4
},
"offset": {
"type": "uint32",
"id": 5
},
"limit": {
"type": "uint32",
"id": 6
},
"algoType": {
"type": "uint32",
"id": 7
}
}
},
"GetARModelListResp": {
"fields": {
"modelList": {
"rule": "repeated",
"type": "ARModel",
"id": 1
}
}
},
"GenerateARModelReq": {
"fields": {
"bizuin": {
"type": "uint32",
"id": 1
},
"name": {
"type": "string",
"id": 2
},
"buffer": {
"type": "bytes",
"id": 3
},
"url": {
"type": "string",
"id": 4
},
"algoType": {
"type": "enARAlgorithmType",
"id": 5
},
"lod": {
"type": "uint32",
"id": 6,
"options": {
"default": 0
}
},
"getmesh": {
"type": "bool",
"id": 7,
"options": {
"default": false
}
},
"gettexture": {
"type": "bool",
"id": 8,
"options": {
"default": false
}
}
}
},
"GenerateARModelResp": {
"fields": {
"url": {
"type": "string",
"id": 1
},
"host": {
"type": "string",
"id": 2
},
"cosid": {
"type": "string",
"id": 3
},
"lod": {
"type": "uint32",
"id": 4,
"options": {
"default": 0
}
},
"getmesh": {
"type": "bool",
"id": 5,
"options": {
"default": false
}
},
"gettexture": {
"type": "bool",
"id": 6,
"options": {
"default": false
}
}
}
},
"ARModelData": {
"fields": {
"meshModel": {
"type": "bytes",
"id": 1
},
"textureModel": {
"type": "bytes",
"id": 2
},
"preview": {
"type": "bytes",
"id": 3
},
"meshBlob": {
"type": "bytes",
"id": 4
},
"textureBlob": {
"type": "bytes",
"id": 5
}
}
},
"GetARModelReq": {
"fields": {
"bizuin": {
"type": "uint32",
"id": 1
},
"cosid": {
"type": "string",
"id": 2
},
"modelType": {
"type": "uint32",
"id": 3
},
"needData": {
"type": "uint32",
"id": 4,
"options": {
"default": 1
}
},
"useIntranet": {
"type": "uint32",
"id": 5,
"options": {
"default": 0
}
},
"expireTime": {
"type": "uint32",
"id": 6
}
}
},
"GetARModelResp": {
"fields": {
"modelData": {
"type": "ARModelData",
"id": 1
},
"url": {
"type": "string",
"id": 2
},
"host": {
"type": "string",
"id": 3
},
"errMsg": {
"type": "string",
"id": 4
},
"expireTime": {
"type": "uint32",
"id": 5
},
"status": {
"type": "uint32",
"id": 6
}
}
}
}
}

@ -0,0 +1,34 @@
// 模块导出一个数组,每个元素是一个模块配置项
module.exports = [
{
// 模块对应的 proto 文件相对于该文件的路径
proto: './proto/mmbizwxaintparDemo.proto',
// 模块 service name
serviceName: 'Mmbizwxaintpar',
// 模块 magic 数字
magic: 11081,
// 模块导出的接口方法
functions: {
// 接口名字及其对应的接口调用信息
GenerateARModel: {
// 接口的 cmdid
cmdid: 1,
// 接口的 request 对应的 protobuf message 名字,需在 proto 文件中定义
req: 'GenerateARModelReq',
// 接口的 response 对应的 protobuf message 名字,需在 proto 文件中定义
res: 'GenerateARModelResp',
},
// 接口的名字及其对应的接口调用信息
GetARModelList: {
cmdid: 3,
req: 'GetARModelListReq',
res: 'GetARModelListResp',
},
GetARModel: {
cmdid: 4,
req: 'GetARModelReq',
res: 'GetARModelResp',
}
}
}
]

@ -0,0 +1,12 @@
## 0.6.0
1. `A` 新增 `callWXSvrkit` 支持传入 `headuin``routeMethod`
## 0.2.0
1. `A` 新增 `callTencentInnerAPI` 内部接口
## 0.1.0
1. `A` 新增 `callWXSvrkit` 内部接口
2. `A` 新增 `callWXInnerAPI` 内部接口

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2018 wechat-miniprogram
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,326 @@
# WXG Service for Mini Program Server SDK
## wx-server-sdk 插件使用方式
需同时安装依赖 `wx-server-sdk``@tencent/wx-server-sdk-wxg-service`,后者必须使用 `tnpm` 安装。
注册 SDK 插件的示例代码:
```js
const cloud = require('wx-server-sdk')
const wxgService = require('@tencent/wx-server-sdk-wxg-service')
// 将 wxg service 注册到 cloud 上,获得 callWXSvrkit, callTencentInnerAPI 等内部接口
cloud.registerService(wxgService)
exports.main = async (event, context) => {
// 现在可以使用 callWXSvrkit 等内部接口了
cloud.callWXSvrkit({
// ....
})
}
```
## 兼容性说明
`0.6.1` 起,需搭配 `wx-server-sdk` `2.0.3` 或以上使用。
## 接口
WXG Service 包含以下接口
### callTencentInnerAPI调用腾讯内部服务
```js
const cloud = require('wx-server-sdk')
const wxgService = require('@tencent/wx-server-sdk-wxg-service')
// 将 wxg service 注册到 cloud 上,获得 callWXSvrkit 接口
cloud.registerService(wxgService)
exports.main = async (event, context) => {
const callResult = await cloud.callTencentInnerAPI({
cmdid: 12345, // 必填
modid: 67890, // 必填
path: '/aaa/bbb?xxx=1', // 必填,除域名外 URL 路径。URL 参数选填
https: false, // 选填,是否使用 https
method: 'post', // 必填HTTP Method
// 选填HTTP 头部
headers: {
'Content-Type': 'application/json'
},
// 选填body 可以是 string 类型或 Buffer 类型
body: JSON.stringify({
x: 1,
y: 2,
}),
/**
* autoParse:
* 是否自动 parse 回包包体,如果是,则:
* 在 content-type 为 application/json 时自动 parse JSON
* 在 content-type 为 text/plain 时自动转为 string
* 其余情况不 parse返回原始包体 buffer
*/
autoParse: true,
})
console.log(callResult)
/**
* callResult:
* {
* errCode: 0,
* errMsg: 'callTencetnInnerAPI:ok',
* contentType: 'application/json', // 回包 content-type
* body: { x: 1 }, // 回包 http response body
* statusCode: 200, // 回包 http status code
* rawHeaders: [ // 回包 http headers
* {
* key: 'content-type',
* value: 'application'
* },
* // ...
* ]
* }
*/
return callResult
}
```
### callWXSvrkit调用微信 Svrkit 服务
供 WXG 内部小程序通过云函数调用微信 svrkit 模块。
因为 svrkit 的数据交换协议是 `protobuf`,且 svrkit 的模块调用需要一些模块信息因此为了简化前端调用方式、省去接口数据处理和调用信息处理pb 序列化与反序列化、模块信息传入)、我们也提供了一个 `@tencent/cloud-functions-tools` 工具用于将 svrkit 的调用流程配置化、标准化,开发者只需填写配置文件和放置 `proto` 文件,即可用工具生成辅助模块,实际调用时即可传入 `JSON` 对象获取 `JSON` 返回值。
示例云函数已放置在公开的内部 git 仓库,仓库 `cloudfunctions` 目录下有两个子目录分别是 `svrkit-echo``svrkit-check-developer`这两个示例云函数,仓库地址 https://git.code.oa.com/mp-public/cloud-demos
### Step 1安装 `@tencent/cloud-functions-tools`
首先在需要使用该能力的云函数的目录下安装 `@tencent/cloud-functions-tools`
```shell
npm install --save-dev @tencent/cloud-functions-tools@latest
```
注意,云函数中同时需安装 `wx-server-sdk``@tencent/wx-server-sdk-wxg-service`
```bash
npm install --save wx-server-sdk@latest
npm install --save @tencent/wx-server-sdk-wxg-service@latest
```
### Step 2配置
在云函数目录下建立 `svrkit.config.js` 文件,用于填写 `svrkit` 调用的配置信息,并放置好相应 proto 文件,示例如下(从后台的原始 pb 文件提取模块调用信息的方法参见底部的[svrkit 调用信息提取](#extract_pb_info)
```js
// 模块导出一个数组,每个元素是一个模块配置项
module.exports = [
{
// 模块对应的 proto 文件相对于该文件的路径
proto: './proto/mmbizwxaqbasedemo.proto',
// 模块 service name
serviceName: 'MMBizWxaQbaseDemo',
// 模块 magic 数字
magic: 18501,
// 模块导出的接口方法
functions: {
// 接口 EchoTest 的名字及其对应的接口调用信息
EchoTest: {
// 接口的 cmdid
cmdid: 1,
// 接口的 request 对应的 protobuf message 名字,需在 proto 文件中定义
req: 'EchoTestRequest',
// 接口的 response 对应的 protobuf message 名字,需在 proto 文件中定义
res: 'EchoTestResponse',
},
// 接口 CheckWxaDeveloper 的名字及其对应的接口调用信息
CheckWxaDeveloper: {
cmdid: 2,
req: 'CheckWxaDeveloperRequest',
}
}
}
]
```
示例的 `./proto/mmbizwxaqbasedemo.proto` 文件:
```
message MessageData
{
optional string Data = 1;
}
message EchoTestRequest
{
required bool OpenTime = 1; // 是否需要服务端返回时间戳required 必填
// 以下 optional 字段如果有填在Resp中返回对应字段
optional int32 IntData = 2;
optional string StringData = 3;
repeated uint32 UintListData = 4;
optional MessageData MessageData = 5;
}
message EchoTestResponse
{
optional uint32 TimeStamp = 1;
optional int32 IntRespData = 2;
optional string StringRespData = 3;
repeated uint32 UintListRespData = 4;
optional MessageData MessageRespData = 5;
}
// 校验是否为小程序开发者, 接口返回0 是1 否
message CheckWxaDeveloperRequest
{
optional uint32 appuin = 1;
optional uint32 useruin = 2;
}
```
### Step 3用工具生成辅助类
`@tencent/cloud-functions-tools` 提供的工具根据 `svrkit.config.js` 生成辅助的 `svrkit-utils.js` 模块:
```bash
# 在云函数目录执行以下命令,--config (或 -c 对应的路径如果不同则替换为自己的路径
./node_modules/.bin/svrkit-utils --config ./svrkit.config.js
# 如需自定义输出文件的路径,可以传入 --output (或 -o
./node_modules/.bin/svrkit-utils --config ./svrkit.config.js --output ./svrkit-utils.js
```
> 注:可用此命令查看 cloud-functions-tools 的用法:./node_modules/.bin/svrkit-utils -h
可以在 `package.json` 中添加一个 `script` 命令自动化这个流程:
```json
{
"scripts": {
"svrkit": "svrkit-utils --config ./svrkit.config.js --output ./svrkit-utils.js"
}
}
```
然后之后就可以用如下命令生成 js 模块了:
```bash
npm run svrkit
```
### Step 4发起调用
`callWXSvrkit` 方法配合生成的 `svrkit-utils.js` 模块发起 svrkit 调用
**callWXSvrkit 接口参数说明**
| 参数 | 类型 | 必填 | 说明 |
| ---------- | ---- | ---- | --------------------------------------------------- |
| pbInstance | | 是 | 通过 `svrkit-utils.js``generate` 方法生成的对象 |
| timeout | number | 否 | 超时失败时间 |
**callWXSvrkit 接口返回值说明**
| 参数 | 类型 | 说明 | 最低版本 |
| -------------- | ------ | ------------------------------------------------------------ | -------- |
| errMsg | String | 通用返回结果 | |
| ret | Number | svrkit 模块调用结果0 表示成功调到了目标模块,-306 表示小程序未在内部小程序登记平台登记,-307 表示小程序没有调用目标模块的权限 | |
| result | Number | 目标模块的调用返回码 | |
| respBody | Object | 目标模块返回的最终 JSON 对象 | |
| respBodyBuffer | Buffer | 目标模块返回的 pb buffer有 respBody 可不用关注 | |
调用代码示例:
```js
const cloud = require('wx-server-sdk')
const wxgService = require('@tencent/wx-server-sdk-wxg-service')
cloud.registerService(wxgService)
cloud.init()
const svrkitUtils = require('./svrkit-utils.js')
exports.main = async (event, context) => {
return await cloud.callWXSvrkit({
pbInstance: svrkitUtils.generate({
serviceName: 'MMBizWxaQbaseDemo',
funcName: 'EchoTest',
data: {
OpenTime: event.OpenTime || true,
IntData: event.IntData || 10,
StringData: event.StringData || 'default_string',
UintListData: event.UintListData || [1, 2, 3],
MessageData: event.MessageData || {
Data: 'default_data_string'
},
}
})
})
}
```
预期返回:
```js
{
"ret": 0,
"result": 0,
"respBodyBuffer": Buffer,
"respBody": {
"TimeStamp": 1543501760,
"IntRespData": 10,
"StringRespData": "default_string",
"UintListRespData": [
1,
2,
3
],
"MessageRespData": {
"Data": "default_data_string"
}
},
"errMsg": "callWXSvrkit:ok"
}
```
调用数据流如图:
![svrkit 调用数据流](http://km.oa.com/files/photos/pictures//20181130//1543559018_58.png)
<a id="extract_pb_info"></a>
从后台原始 svrkit pb 信息中提取模块调用信息:
1. `message` 定义的是 pb 数据接口体的定义,通常即是请求包与回包的定义
2. `service` 定义的是 svrkit 模块的 `serviceName`
3. `tlvpickle.Magic` 定义的是模块 `magic`
4. `rpc` 后跟的词即是模块暴露的接口方法名,即 `funcName`
5. `rpc` 定义中包含了请求体结构和返回体结构,如 `EchoTest` 方法的请求体结构为 `EchoTestRequest`,返回体结构为 `EchoTestResponse`
6. `tlvpickle.SKBuiltinEmpty_PB` 是特殊的结构体,表示空结构体
![svrkit 模块调用信息](http://km.oa.com/files/photos/captures/201812/1543846169_62_w1668_h2228.png)
### callSvrkit: 调用微信 Svrkit 服务
> 已废弃,请使用 callWXSvrkit

File diff suppressed because it is too large Load Diff

@ -0,0 +1,11 @@
{
"name": "@tencent/wx-server-sdk-wxg-service",
"version": "0.7.0",
"description": "wxg service plugin for mini program cloud server sdk",
"main": "index.js",
"author": "wechat mini program team",
"license": "MIT",
"dependencies": {
"tslib": "^1.9.3"
}
}

@ -1,17 +0,0 @@
const cloud = require('wx-server-sdk');
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
});
// 获取openId云函数入口函数
exports.main = async (event, context) => {
// 获取基础信息
const wxContext = cloud.getWXContext();
return {
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
};
};

@ -0,0 +1,13 @@
// 云函数入口文件
const cloud = require('wx-server-sdk')
// 云函数入口函数
exports.main = async () => {
cloud.init({
env: process.env.TCB_ENV,
})
const db = cloud.database()
return db.collection('perm4').where({
_openid: 'server'
}).limit(1).get()
}

@ -0,0 +1,14 @@
{
"name": "getServerDataDemo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"wx-server-sdk": "latest"
}
}

@ -0,0 +1,14 @@
// 云函数入口文件
const cloud = require('wx-server-sdk')
// 云函数入口函数
exports.main = async (event) => {
cloud.init({
env: process.env.TCB_ENV,
})
const fileList = event.fileIdList
const result = await cloud.getTempFileURL({
fileList,
})
return result.fileList
}

@ -0,0 +1,14 @@
{
"name": "getTempFileURL",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"wx-server-sdk": "latest"
}
}

@ -0,0 +1,3 @@
exports.main = (event) => ({
openid: event.userInfo.openId,
})

@ -1,5 +1,5 @@
{
"name": "sendComment",
"name": "login",
"version": "1.0.0",
"description": "",
"main": "index.js",
@ -9,6 +9,6 @@
"author": "",
"license": "ISC",
"dependencies": {
"wx-server-sdk": "~2.6.3"
"wx-server-sdk": "latest"
}
}

@ -0,0 +1,19 @@
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) // 使用当前云环境
// 云函数入口函数
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
if (event.op == "")
return {
event,
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
}
}

@ -0,0 +1,21 @@
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) // 使用当前云环境
const db = wx.cloud.database().collection("orders")
const wxContext = cloud.getWXContext()
// 云函数入口函数
exports.main = async () => {
const result;
db.where({
_openid: wxContext.OPENID
}).get({
success: function (res) {
result = res
}
})
return result
}

@ -0,0 +1,20 @@
// 云函数入口文件
// const cloud = require('wx-server-sdk')
const get = require("./get")
const add = require("./add")
// cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) // 使用当前云环境
// 云函数入口函数
exports.main = async (event, context) => {
// const wxContext = cloud.getWXContext()
if (event.op == "get") {
return await get.main();
} else if(event.op == "add") {
add()
//TODO add paramas
return
}
return true
}

@ -1,5 +1,5 @@
{
"name": "getOpenId",
"name": "orders",
"version": "1.0.0",
"description": "",
"main": "index.js",

@ -1,25 +0,0 @@
// 云函数入口文件
const cloud = require('wx-server-sdk');
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
});
//const db = cloud.database();
// 云函数入口函数
exports.main = async (val, comment) => {
  return await cloud.database().collection("comment").doc(event.id)
  .update({
      data:{  
comment: comment_text      
      }
  }) 
  .then(res =>{
      console.log("改变评论状态成功1",res)
      return res
  })
  .catch(res =>{
      console.log("改变评论状态失败",res)
      return res
  })
}

@ -24,11 +24,18 @@ App({
//尝试读取本地缓存
try {
// wx.clearStorage()
this.globalData.USERID = wx.getStorageSync('user_id')
// this.globalData.USERID = wx.getStorageSync('user_id')
const avatar = wx.getStorageSync('avatarUrl')
if (!avatar) {
wx.setStorageSync('avatarUrl', "")
}
this.globalData.OPENID = wx.getStorageSync('user_openid')
} catch (e) {
console.error(e);
}
const res = wx.getStorageInfoSync()
// console.log(res);
console.log("[GLOABLE] Cache:",res.keys);
//检测用户是否首次登录
if (!this.globalData.OPENID) {

@ -1,12 +1,11 @@
{
"pages": [
"pages/Main/main",
"pages/Msg/msg",
"pages/Profile/profile",
"pages/Tprofile/Tprofile",
"pages/Comments/Comments",
"pages/Order/order",
"pages/Details/details"
"pages/Details/detail",
"pages/Comments/comments"
],
"window": {
"backgroundColor": "#FFF",
@ -19,21 +18,21 @@
"list": [
{
"pagePath": "pages/Main/main",
"text": "main",
"iconPath": "images/main.png",
"selectedIconPath": "images/main_on.png"
"text": "主页",
"iconPath": "images/home.png",
"selectedIconPath": "images/home-fill.png"
},
{
"pagePath": "pages/Order/order",
"text": "msg",
"iconPath": "images/msg.png",
"selectedIconPath": "images/msg_on.png"
"text": "订单",
"iconPath": "images/business-icon-feeds.png",
"selectedIconPath": "images/business-icon-feeds-fill.png"
},
{
"pagePath": "pages/Profile/profile",
"text": "profile",
"iconPath": "images/profile.png",
"selectedIconPath": "images/profile_on.png"
"text": "我的",
"iconPath": "images/customer.png",
"selectedIconPath": "images/customer-fill.png"
}
]
},

@ -0,0 +1,30 @@
// components/grid-tile/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
height: {
type: Number,
value: 0,
},
index: {
type: Number,
value: 0,
},
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
}
})

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

@ -0,0 +1,5 @@
<view class="center" style="width: 100%; height: {{height}}px; background-color: #6bbc7a;">
<view class="center" style="width: 60px; height: 60px; border-radius: 30px; background-color: white; color: black;">
{{index}}
</view>
</view>

@ -0,0 +1,10 @@
/* components/grid-tile/index.wxss */
:host {
display: block;
}
.center {
text-align: center;
align-items: center;
justify-content: center;
}

@ -0,0 +1,70 @@
Component({
options: {
multipleSlots: true, // 在组件定义时的选项中启用多slot支持
},
/**
* 组件的属性列表
*/
properties: {
title: {
type: String,
value: '',
},
background: {
type: String,
value: '',
},
color: {
type: String,
value: '',
},
back: {
type: Boolean,
value: true,
},
loading: {
type: Boolean,
value: false,
},
// back为true的时候返回的页面深度
delta: {
type: Number,
value: 1,
},
sideWidth: {
type: Number,
value: 0,
},
},
attached() {
const isSupport = !!wx.getMenuButtonBoundingClientRect;
const rect = wx.getMenuButtonBoundingClientRect ? wx.getMenuButtonBoundingClientRect() : null;
wx.getSystemInfo({
success: (res) => {
const ios = !!(res.system.toLowerCase().search('ios') + 1);
const sideWidth = isSupport ? res.windowWidth - rect.left : 0;
this.setData({
ios,
sideWidth: this.data.sideWidth || sideWidth,
statusBarHeight: res.statusBarHeight,
});
},
});
},
/**
* 组件的方法列表
*/
methods: {
back() {
const { data } = this;
if (data.delta) {
wx.navigateBack({
delta: data.delta,
});
}
this.triggerEvent('back', { delta: data.delta }, {});
},
},
});

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

@ -0,0 +1,26 @@
<view class="navigation-bar">
<view class="navigation-bar__inner {{ios ? 'ios' : 'android'}}" style="margin-top: {{statusBarHeight}}px; color: {{color}};background: {{background}}">
<view class='navigation-bar__left' style="width: {{sideWidth}}px;">
<block wx:if="{{back}}">
<view class="navigation-bar__buttons" bindtap="back" >
<image class="navigation-bar__button navigation-bar__btn_goback" hover-class="active" src="../../images/back-arrow.png"></image>
</view>
</block>
<block wx:else>
<slot name="left"></slot>
</block>
</view>
<view class="navigation-bar__center {{title ? 'title' : ''}}">
<view wx:if="{{loading}}" class="navigation-bar__loading">
<view class="loading"></view>
</view>
<block wx:if="{{title}}">{{title}}</block>
<block wx:else>
<slot name="center"></slot>
</block>
</view>
<view class='navigation-bar__right' style="width: {{sideWidth}}px;">
<slot name="right"></slot>
</view>
</view>
</view>

@ -0,0 +1,75 @@
.navigation-bar {
background-color: #f7f7f7;
}
.navigation-bar .android {
height: 48px;
}
.navigation-bar__inner {
height: 44px;
display: flex;
flex-direction: row;
align-items: center;
}
.navigation-bar__inner .navigation-bar__left {
position: relative;
padding-left: 16px;
height: 100%;
display: flex;
flex-direction: row;
align-items: center;
}
.navigation-bar__inner .navigation-bar__left .navigation-bar__buttons {
height: 100%;
flex-direction: row;
align-items: center;
min-width: 50px;
}
.navigation-bar__inner .navigation-bar__left .navigation-bar__btn {
display: inline-block;
vertical-align: middle;
background-repeat: no-repeat;
height: 100%;
flex-direction: row;
align-items: center;
}
.navigation-bar__inner .navigation-bar__left .navigation-bar__btn_goback {
width: 8px;
height: 14px;
}
.navigation-bar__inner .navigation-bar__center {
font-size: 17px;
text-align: center;
position: relative;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.navigation-bar__inner .navigation-bar__center.title {
display: inline-block;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.navigation-bar__inner .navigation-bar__loading {
margin-right: 4px;
display: inline-flex;
align-items: center;
}
.navigation-bar__inner .navigation-bar__loading .loading {
margin-left: 0;
}
.navigation-bar__inner .navigation-bar__right {
padding-right: 16px;
}

@ -0,0 +1,89 @@
var globalThis = this, self = this;
module.exports =
require("../../_commons/0.js")([
{
"ids": [6],
"modules":{
/***/ "./node_modules/@mpflow/webpack-plugin/lib/loaders/page-loader.js?appContext=src&outputPath=components%2Fpage-scroll%2Findex!./src/components/page-scroll/index.ts":
/*!*************************************************************************************************************************************************************************!*\
!*** ./node_modules/@mpflow/webpack-plugin/lib/loaders/page-loader.js?appContext=src&outputPath=components%2Fpage-scroll%2Findex!./src/components/page-scroll/index.ts ***!
\*************************************************************************************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(/*! ./index.ts */ "./src/components/page-scroll/index.ts")
/***/ }),
/***/ "./src/components/page-scroll/index.ts":
/*!*********************************************!*\
!*** ./src/components/page-scroll/index.ts ***!
\*********************************************/
/*! no static exports found */
/***/ (function(module, exports) {
Component({
options: {
virtualHost: true
},
properties: {
refresherEnabled: {
type: Boolean,
value: false
},
refresherThreshold: {
type: Number,
value: 45
},
refresherDefaultStyle: {
type: String,
value: 'black'
},
refresherBackground: {
type: String,
value: '#FFF'
},
refresherTriggered: {
type: Boolean,
value: false
},
lowerThreshold: {
type: Number,
value: 50
},
scrollIntoView: {
type: String,
value: ''
}
},
methods: {
onScroll: function onScroll(e) {
this.triggerEvent('scroll', e.detail);
},
onScrollToLower: function onScrollToLower(e) {
this.triggerEvent('scrollToLower', e.detail);
},
onPulling: function onPulling(e) {
this.triggerEvent('pulling', e.detail);
},
onRefresh: function onRefresh(e) {
this.triggerEvent('refresh', e.detail);
},
onRestore: function onRestore(e) {
this.triggerEvent('restore', e.detail);
},
onAbort: function onAbort(e) {
this.triggerEvent('abort', e.detail);
}
}
});
/***/ })
},
"entries": [["./node_modules/@mpflow/webpack-plugin/lib/loaders/page-loader.js?appContext=src&outputPath=components%2Fpage-scroll%2Findex!./src/components/page-scroll/index.ts",0]]
},
]);
// # sourceMappingURL=index.js.map

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {},
"componentFramework": "glass-easel",
"renderer": "skyline"
}

@ -0,0 +1,5 @@
<scroll-view style="flex: 1; overflow: auto; width: 100%;" scroll-y scroll-anchoring enable-back-to-top enhanced scroll-into-view="{{ scrollIntoView }}" refresher-enabled="{{ refresherEnabled }}" refresher-threshold="{{ refresherThreshold }}" refresher-default-style="{{ refresherDefaultStyle }}" refresher-background="{{ refresherBackground }}" refresher-triggered="{{ refresherTriggered }}" bindscroll="onScroll" lower-threshold="{{ lowerThreshold }}" bindscrolltolower="onScrollToLower" bindrefresherpulling="onPulling" bindrefresherrefresh="onRefresh" bindrefresherrestore="onRestore" bindrefresherabort="onAbort" type="list">
<view>
<slot/>
</view>
</scroll-view>

@ -0,0 +1,4 @@
/* # sourceMappingURL=index.wxss.map */

@ -0,0 +1,38 @@
Component({
properties: {
maskClosable: {
type: Boolean,
value: true,
},
mask: {
// 是否需要 遮罩层
type: Boolean,
value: true,
},
maskStyle: {
// 遮罩层的样式
type: String,
value: '',
},
show: {
// 是否开启弹窗
type: Boolean,
value: false,
},
},
data: {
enable: true,
},
methods: {
close() {
const { data } = this;
console.log('@@@ close', data.maskClosable)
if (!data.maskClosable) return;
this.setData({
enable: !this.data.enable
});
this.triggerEvent('close', {}, {});
},
// stopEvent() {},
},
});

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

@ -0,0 +1,5 @@
<root-portal wx:if="{{show}}" enable="{{enable}}">
<view class="popup" bindtap="close">
<slot></slot>
</view>
</root-portal>

@ -0,0 +1,22 @@
root-portal {
display: flex;
width: 100%;
height: 100%;
}
.popup {
position: absolute;
bottom: 0;
z-index: 5000;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
width: 100vw;
height: 200px;
background: rgba(51, 51, 51, 0.65);
opacity: 1;
transform: scale3d(1, 1, 1);
transition: all .2s ease-in;
pointer-events: auto;
}

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="8px" height="14px" viewBox="0 0 8 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>☀ iOS/☀ 图标/线型/icons_outlined_arrow@3x</title>
<g id="控件" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" fill-opacity="0.3">
<g id="4.列表/z.覆盖层/右边/箭头" transform="translate(-334.000000, -21.000000)" fill="#000000">
<g id="☀-iOS/☀-图标/线型/icons_outlined_arrow" transform="translate(332.000000, 16.000000)">
<path d="M2.45405845,6.58064919 L3.51471863,5.51998901 L9.29361566,11.298886 C9.68374096,11.6890113 9.6872014,12.318069 9.29361566,12.7116547 L3.51471863,18.4905518 L2.45405845,17.4298916 L7.87867966,12.0052704 L2.45405845,6.58064919 Z" id="Combined-Shape"></path>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 906 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

@ -1,271 +1,195 @@
const db = wx.cloud.database().collection("comment")
// pages/Detailed/detailed.js
let id=''
let speak=''
let db = wx.cloud.database();
Page({
data: {
//评论数据
comment_list: [
{
comment_id: 1, //评论id
comment_pr_id: 1,//评论文章所属id
comment_user_avatar: 'cloud://cloud1-7gyjwcyfbdf819da.636c-cloud1-7gyjwcyfbdf819da-1321167991/1678762683308.png', //评论用户头像(路径替换为你的图片路径)
comment_user_name: '高同学', //评论人昵称
comment_text: '您觉得这位老师的讲课方式是什么样的呢', //评论内容
comment_time: '2020年8月18日', //评论时间
reply_id: 0, //回复谁的评论默认为0
parent_id: 0, //评论所属评论id默认为0
reply_name: '' //回复评论用户的昵称 默认为''
},
{
comment_id: 2,
comment_pr_id: 1,
comment_user_avatar: 'cloud://cloud1-7gyjwcyfbdf819da.636c-cloud1-7gyjwcyfbdf819da-1321167991/photo/70eb920703ebdb368681f011c75aef3.jpg',
comment_user_name: '王同学',
comment_text: '强烈推荐这位老师,讲课幽默生动,容易接受',
comment_time: '2020年8月18日',
reply_id: 0,
parent_id: 0,
reply_name: ''
},
{
comment_id: 3,
comment_pr_id: 1,
comment_user_avatar: 'cloud://cloud1-7gyjwcyfbdf819da.636c-cloud1-7gyjwcyfbdf819da-1321167991/photo/4494c78cc34e0567b59bd6431df61f6.jpg',
comment_user_name: '张剑锋',
comment_text: '老师讲的比较仔细,但好像不太适合我',
comment_time: '2020年8月18日',
reply_id: 0,
parent_id: 0,
reply_name: ''
}
],
//回复数据
comment_list2: [
{
comment_id: 4,
comment_pr_id: 1,
comment_user_name: '张同学',
comment_user_avatar: '/images/assemblyNumber/discoveryDetails/per3.png',
comment_text: "为什么呢?",
comment_time: '2020年8月18日',
reply_id: 3,
parent_id: 3,
reply_name: ''
},
{
comment_id: 5,
comment_pr_id: 1,
comment_user_name: '沈非隆',
comment_user_avatar: '/images/assemblyNumber/discoveryDetails/per4.png',
comment_text: "办理优待证大概需要多长时间呢会不会需要特别长时间",
comment_time: '2020年8月18日',
reply_id: 3,
parent_id: 3,
reply_name: '张剑锋'
}
],
/*定义一些数据*/
focus: false, //输入框是否聚焦
placeholder: '说点什么...', //底部输入框占字符
placeholder2: '说点什么让ta也认识看笔记的你', //顶部输入框占字符
value: null, //顶部输入框内容
comment_text: null, //底部评论框内容
/*
*以下初始化数据是用户点击任意一条评论或回复时需要设置的数据
*然后将设置好的数据传递给评论时新创建的评论数据对象
*/
now_reply_name: null, //当前点击的评论或回复评论的用户昵称
now_reply_type: 0, //当前回复类型 默认为0 1为回复评论 2为回复回复
now_parent_id: 0, //当前点击的评论或回复评论的所属评论id
now_reply: 0, //当前点击的评论或回复评论的id
//模拟用户信息
userinfo: {
nickName: '马飞', //用户昵称
avatarUrl: '/images/assemblyNumber/discoveryDetails/per5.png' //用户头像
}
},
//顶部评论框提交内容时触发
bindconfirm(e) {
var comment_text = e.detail.value //判断用户是否输入内容为空
if (comment_text == '') { //用户评论输入内容为空时弹出
wx.showToast({
title: '请输入内容', //提示内容
icon: 'none' })
} else {
var date = new Date(); //创建时间对象
var year = date.getFullYear(); //获取年
var month = date.getMonth() + 1; //获取月
var day = date.getDate(); //获取日
var hour = date.getHours(); //获取时
var minute = date.getMinutes(); //获取分
var second = date.getSeconds(); //获取秒
var time = `${year}${month}${day}${hour}${minute}${second}`; //当前时间
var comment_list = this.data.comment_list; //获取data中的评论列表
var comment_list2 = this.data.comment_list2; //获取data中的回复列表
var userinfo = this.data.userinfo; //获取当前的用户信息
var comment_user_name = userinfo.nickName //用户昵称
var comment_user_avatar = userinfo.avatarUrl //用户头像 //
var reply_id = this.data.reply_id //获取当前输入评论的id
var comment_list_length = comment_list.length; //获取当前评论数组的长度
var last_id = comment_list[comment_list_length - 1].comment_id; //获取最后一个评论的id
var comment_list2_length = comment_list2.length; //获取回复数组的长度
var last_id2 = comment_list2[comment_list2_length - 1].comment_id; //获取最后回复id
var new_id = last_id > last_id2 ? last_id + 1 : last_id2 + 1; //赋值当前评论的id
var reply_name = null;
var parent_id = 0;
var reply_id = this.data.now_reply; //获取当前输入评论的id
var comment_detail = {} //声明一个评论/回复对象
comment_detail.comment_id = new_id; //评论Id
comment_detail.comment_user_name = comment_user_name; //用户昵称
comment_detail.comment_user_avatar = comment_user_avatar; //用户头像
comment_detail.comment_text = comment_text; //评论内容
comment_detail.comment_time = time; //评论时间
comment_detail.reply_id = reply_id; //回复谁的评论的id
comment_detail.parent_id = parent_id; //评论所属哪个评论id
comment_detail.reply_name = reply_name; //回复评论人的昵称
comment_list.unshift(comment_detail);
this.setData({
value: null, //评论内容
now_reply: 0, //当前点击的评论id
now_reply_name: null, //当前点击的评论的用户昵称
now_reply_type: 0, //评论类型
now_parent_id: 0, //当前点击的评论所属哪个评论id
placeholder2: "说点什么让ta也认识看笔记的你", //输入框占字符
comment_list //评论列表
})
}
/**
* 页面的初始数据
*/
data: {
good:[],
plcaceHolder:'',
speak:'',
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
// 获取id 根据id获取数据
onLoad(options) {
console.log(options)
id=options.id
this.getgood()
},
//点击用户评论或回复时触发
replyComment(e) {
var cid = e.currentTarget.dataset.cid; //当前点击的评论id
var name = e.currentTarget.dataset.name; //当前点击的评论昵称
var pid = e.currentTarget.dataset.pid; //当前点击的评论所属评论id
var type = e.currentTarget.dataset.type; //当前回复类型
this.setData({
focus: true, //输入框获取焦点
placeholder: '回复' + name + '', //更改底部输入框占字符
now_reply: cid, //当前点击的评论或回复评论id
now_reply_name: name, //当前点击的评论或回复评论的用户名
now_parent_id: pid, //当前点击的评论或回复评论所属id
now_reply_type: type, //获取类型(1回复评论/2回复-回复评论)
getgood(){
let that=this
wx.cloud.database().collection('teacher_Data').doc(id).get()
.then(res=>{
that.setData({
good:res.data
})
})
},
//底部输入框提交内容时触发
confirm(e){
//获取输入框输入的内容
var comment_text = e.detail.value;
//判断用户是否输入内容为空
if (comment_text == '') {
//用户评论输入内容为空时弹出
wx.showToast({
title: '请输入内容', //提示内容
})
} else {
var date = new Date(); //创建时间对象
var year = date.getFullYear(); //获取年
var month = date.getMonth() + 1; //获取月
var day = date.getDate(); //获取日
var hour = date.getHours(); //获取时
var minute = date.getMinutes(); //获取分
var second = date.getSeconds(); //获取秒
var time = `${year}${month}${day}${hour}${minute}${second}`; //当前时间
var userinfo = this.data.userinfo; //获取当前的用户信息
var comment_user_name = userinfo.nickName //用户昵称
var comment_user_avatar = userinfo.avatarUrl //用户头像
var reply_name = null; //回复评论用户的昵称
var parent_id = 0; //评论所属哪个评论的id
var reply_id = this.data.now_reply; //回复谁的评论id
var comment_list = this.data.comment_list; //获评论数据
var comment_list2 = this.data.comment_list2; //获取回复数据
var comment_list_length = comment_list.length; //获取当前评论数组的长度
var last_id = comment_list[comment_list_length - 1].comment_id; //获取最后一个评论的id
var comment_list2_length = comment_list2.length; //获取回复数组的长度
var last_id2 = comment_list2[comment_list2_length - 1].comment_id; //获取最后回复的id
var new_id = last_id > last_id2 ? last_id + 1 : last_id2 + 1; //当前将要发表的评论的id
//通过回复谁的评论id判断现在是评论还是回复
if(reply_id != 0) {
//现在是回复
var reply_type = this.data.now_reply_type; //回复类型
//通过回复类型判断是回复评论还是回复回复
if (reply_type == 1) {
//回复评论
parent_id = this.data.now_reply; //回复评论所属评论id
reply_name = this.data.now_reply_name; //回复评论用户昵称
} else {
parent_id = this.data.now_parent_id; //回复评论所属评论id
reply_name = this.data.now_reply_name; //回复评论用户昵称
}
} else {
//现在是评论
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
// 获取评价信息
getInputValue(event){
speak=this.data.plcaceHolder + event.detail.value
},
// 发送评价信息
publishComment(){
let that=this
if(!that.data.good.speaklist){
let speaklist=[{
nickname:wx.getStorageSync('user').nickname,
speak:speak,
}]
db.collection('teacher_Data').doc(id).update({
data:{
speaklist:speaklist
}
var comment_detail = {} //评论/回复对象
comment_detail.comment_id = new_id; //评论Id
comment_detail.comment_user_name = comment_user_name; //用户昵称
comment_detail.comment_user_avatar = comment_user_avatar; //用户头像
comment_detail.comment_text = comment_text; //评论内容
comment_detail.comment_time = time; //评论时间
comment_detail.reply_id = reply_id; //回复谁的评论的id
comment_detail.parent_id = parent_id; //评论所属哪个评论id
comment_detail.reply_name = reply_name; //回复评论人的昵称
//判断parent_id是否为0 为0就是评论 不为0就是回复
if(comment_detail.parent_id != 0) {
//回复
comment_list2.unshift(comment_detail);
} else {
//评论
comment_list.unshift(comment_detail);
})
.then(res=>{
that.setData({
speak:''
})
that.getgood()
wx.showToast({
title:'已评价',
icon:'none'
})
})
}
else{
let newspeaklist=that.data.good.speaklist
newspeaklist.push({
nickname:wx.getStorageSync('user').nickname,
speak:speak,
})
console.log(newspeaklist)
db.collection('teacher_Data').doc(id).update({
data:{
speaklist:newspeaklist
}
//动态渲染
ths.setData({
//发表评论后将以下数据初始化 为下次发表评论做准备
comment_text: null, //评论内容
now_reply: 0, //当前点击的评论id
now_reply_name: null, //当前点击的评论的用户昵称
now_reply_type: 0, //评论类型
now_parent_id: 0, //当前点击的评论所属哪个评论id
placeholder: "什么...", //输入框占字符
//将加入新数据的数组渲染到页面
comment_list2 //回复列表
})
}
},
.then(res=>{
that.setData({
speak:''
})
that.getgood()
wx.showToast({
title:'已评价',
icon:'none'
})
})
}
//下面的方法可以绑定在输入框的bindblur属性上
blur(e) {
const text = e.detail.value.trim();
if (text == '') {
this.setData({
now_reply: 0, //当前点击的评论或回复评论的id
now_reply_name: null, //当前点击的评论或回复评论的用户昵称
now_reply_type: 0, //当前回复类型
now_parent_id: 0, //当前点击的评论或回复评论的所属评论id
placeholder: "说点什么呢,万一火了呢", //占字符
focus: false //输入框获取焦点
})
}
},
//获取输入框内容
getCommentText(e) {
var val = e.detail.value;
wx.cloud.callFunction({
name:"sendComment",
data:{
comment :val
}
})
},
// 回复评价
huifuComment(event){
let that=this
var index = event.currentTarget.dataset.index
this.setData({
comment_text: val
plcaceHolder : '回复' + this.data.good.speaklist[index].nickname+':',
})
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
// 获取公司招聘数据
getList(){
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

@ -1,61 +1,30 @@
<!-- 评论-回复-回复评论显示区域 -->
<view class="container">
<!-- 总共评论数 -->
<view class="total">共{{comment_list.length + comment_list2.length}}条评论</view>
<!-- END -->
<!--pages/Detailed/detailed.wxml-->
<!-- 评论框 -->
<!-- <input confirm-type="send" class="container_inp" value="{{value}}" placeholder="{{ placeholder2 }}" placeholder-class="container_place" bindconfirm="bindconfirm"></input> -->
<input confirm-type="send" class="container_inp" value="{{value}}" placeholder="{{ placeholder2 }}" placeholder-class="container_place" bindconfirm="bindconfirm"></input>
<view class="comment-box" wx:if="{{good.speaklist.length > 0}}">
<view class="comment-right">
<block wx:for="{{good.speaklist}}" wx:key="index">
<view class="comment-one" >
<image src="/images/profile.png" class="comment-face" bindtap="huifuComment" data-index="{{index}}"></image>
<view class="comment-content">
<view class="nickname-time">
<view class="comment-nickname">{{item.nickname}}</view>
<!-- END -->
<!-- 用户评论回复显示区域 -->
<view class="container-item" wx:for="{{comment_list}}" wx:for-item="list" wx:key="key">
<image class="item_img" src="{{list.comment_user_avatar}}"></image>
<view class="item_right">
<view class="right_name">{{list.comment_user_name}}</view>
<view class="right_content">
<text class="right_content_txt" bindtap='replyComment' data-type="1" data-name='{{list.comment_user_name}}' data-cid='{{list.comment_id}}' data-pid="{{list.parent_id}}">{{list.comment_text}}</text>
<text class="right_content_txt2">{{list.comment_time}}</text>
<!-- 回复评论 -->
<!-- 判断回复列表数据中的parent_id和评论列表数据的comment_id是否相等 相等就显示 不等就不显示 -->
<view class="reply" wx:for="{{comment_list2}}" wx:for-item="list2" wx:key="list2" wx:if="{{list2.parent_id == list.comment_id}}">
<image class="reply_img" src="{{list2.comment_user_avatar}}"></image>
<view class="reply_right">
<view class="right_name">{{list2.comment_user_name}}</view>
<text wx:if="{{list2.reply_name == ''}}" class="right_content_txt" bindtap='replyComment' data-type="2" data-name='{{list2.comment_user_name}}' data-cid='{{list2.comment_id}}' data-pid="{{list2.parent_id}}">{{list2.comment_text}}</text>
<text wx:if="{{list2.reply_name != ''}}" bindtap='replyComment' data-type="2" data-name='{{list2.comment_user_name}}' data-cid='{{list2.comment_id}}' data-pid="{{list2.parent_id}}" class="right_content_txt">回复 <text class="right_name">{{list2.reply_name}}</text>{{list2.comment_text}}</text>
<text class="right_content_txt2">{{list2.comment_time}}</text>
</view>
<view class="comment-text" selectable="true"bindtap="SCpl" data-index="{{index}}" >
<text wx:if="{{item.toOpenid}}">回复<text class="comment-obj">{{item.toNickname}}</text></text>{{item.speak}}
</view>
</view>
</view>
</view>
</view>
<!-- END -->
<!-- 结束 -->
<view class="end">
<text class="end_txt" space="nbsp">— THE END —</text>
<view class="divLine"></view>
</block>
</view>
<!-- END -->
</view>
<!-- END -->
<!-- 底部评论 -->
<view class="foot">
<view class="say">
<view class="flex">
<!-- <image class="say_img" src=" "></image> -->
<input confirm-type="send" class="say_inp" placeholder="{{ placeholder }}" value="{{ comment_text }}" focus="{{ focus }}" bindblur="blur" bindconfirm="confirm"></input>
</view>
</view>
<!-- <image class="foot_img" src=" "></image> -->
<text class="foot_num">210</text>
<!-- <image class="foot_img2" src=" "></image> -->
<text class="foot_num"> 368</text>
<!-- 底部 -->
<view style="height:200rpx"></view>
<view class="pub-comment" >
<view class="pub-left">
<input class="pub-input" cursor-spacing="10px" placeholder="{{plcaceHolder}}" bindinput="getInputValue" value="{{speak}}" bindconfirm="publishComment"></input>
</view>
<!-- END -->
<view class="pub-button" bindtap="publishComment">发送</view>
</view>

@ -1,158 +1,110 @@
/* 评论-回复-回复评论显示区域 */
.container {
width: 718rpx;
margin-top: 39rpx;
padding-left: 32rpx;
}
.total {
font-size: 24rpx;
color: #999999;
margin-left: 1rpx;
}
.container_inp {
width: 661rpx;
height: 32rpx;
background-color: #F2F2F2;
border-radius: 8rpx;
padding: 17rpx 0rpx 15rpx 25rpx;
margin-top: 24rpx;
margin-bottom: 47rpx;
}
.container_place {
font-size: 24rpx;
color: #999999;
}
.container-item {
display: flex;
margin-bottom: 34rpx;
align-items: flex-start;
}
.item_img {
width: 64rpx;
height: 64rpx;
margin-right: 17rpx;
}
.item_right {
display: inline-block;
width: 636rpx;
border-bottom: 2rpx solid #E5E5E5;
padding-bottom: 40rpx;
}
.right_name {
font-size: 24rpx;
color: #999999;
}
.right_content {
width: 603rpx;
margin-top: 20rpx;
}
.right_content_txt {
font-size: 26rpx;
color: #333333;
}
.right_content_txt2 {
font-size: 24rpx;
color: #CCCCCC;
margin-left: 30rpx;
}
.reply {
display: flex;
margin-top: 32rpx;
align-items: flex-start;
}
.reply_img {
width: 40rpx;
height: 40rpx;
margin-right: 17rpx;
}
.reply_right {
display: inline-block;
width: 546rpx;
}
/* 结束 */
.end {
margin-top: 47rpx;
margin-bottom: 48rpx;
text-align: center;
}
.end_txt {
font-size: 24rpx;
color: #999999;
}
/* 底部评论 */
.foot {
width: 686rpx;
padding: 17rpx 32rpx;
background-color: #FFFFFF;
box-shadow: 0rpx -1rpx 4rpx 0rpx rgba(0, 0, 0, 0.08);
position: fixed;
bottom: 0;
left: 0;
}
.say {
display: inline-block;
width: 360rpx;
padding: 20rpx 0rpx 10rpx 24rpx;
background-color: #F2F2F2;
border-radius: 8rpx;
}
.flex {
display: flex;
align-items: center;
}
.say_img {
width: 24rpx;
height: 24rpx;
margin-right: 13rpx;
/* vertical-align: 10rpx; */
}
.say_inp {
display: inline-block;
width: 300rpx;
height: 33rpx;
}
.say_place {
font-size: 28rpx;
color: #333333;
}
.foot_img {
width: 45rpx;
height: 40rpx;
margin-left: 50rpx;
margin-right: 17rpx;
}
.foot_num {
font-size: 24rpx;
vertical-align: 10rpx;
}
.foot_img2 {
width: 40rpx;
height: 40rpx;
margin-left: 30rpx;
margin-right: 17rpx;
}
/* pages/Detailed/detailed.wxss */
.divLine{
background: #E0E3DA;
width: 90%;
height: 5rpx;
margin-left: 30rpx;
margin-bottom: 20rpx;
}
/* 留言部分 */
.comment-box {
background-color: white;
display: flex;
flex-direction: row;
padding-top: 20rpx;
}
.comment-one {
display: flex;
flex-direction: row;
margin: 0 20rpx 40rpx 20rpx;
}
.comment-face {
width: 70rpx;
height: 70rpx;
border-radius: 50rpx;
margin-right: 20rpx;
}
.nickname-time {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 600rpx;
}
.comment-nickname {
color: #586B94;
font-weight: 550;
font-size: 30rpx;
}
.comment-time {
color: #7F7F7F;
font-size: 26rpx;
}
.comment-text {
margin-top: 14rpx;
font-size: 28rpx;
width: 606rpx;
word-wrap: break-word;
word-break: break-all;
letter-spacing: 2rpx;
display: inline-block;
}
.comment-obj {
color: #586B94;
font-weight: 550;
}
/* 发布评论 */
.pub-comment {
background-color: white;
padding: 20rpx 40rpx;
display: flex;
flex-direction: row;
align-items: center;
position: fixed;
bottom: 0;
border-top: solid 1rpx #E0E3DA;
}
.pub-left {
background-color: #fff;
color: #7F7F7F;
border-radius: 10rpx;
margin-right: 20rpx;
}
.pub-input {
padding: 10rpx 20rpx;
width: 490rpx;
background-color: #F1F1F1;
border-color: #AAAAAA;
border-style: solid;
border-width: 1px;
}
.pub-button {
background-color: #1AAD19;
text-align: center;
color: #ffffff;
border: solid 1rpx #7F7F7F;
border-radius: 10rpx;
padding: 10rpx 18rpx;
}
.comment-right{
width: 100%;
}

@ -0,0 +1,111 @@
// pages/Detailed/detailed.js
const defaultAvatarUrl = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
Page({
/**
* 页面的初始数据
*/
data:{
avatarUrl: defaultAvatarUrl,
teacher: null
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const eventChannel = this.getOpenerEventChannel()
eventChannel.on('acceptDataFromOpenerPage', (data) => {
//对发送过来的数据进行处理
console.log("[DETAILS]",data)
this.setData({
teacher: data.data,
avatarUrl: data.data.headPhoto
})
})
// 使用索引在教师数组中获取对应的教师信息
// var teacherInfo = this.data.teacher[index];
// 继续处理教师信息
// 调用云函数获取教师信息
// wx.cloud.callFunction({
// name: "detailed",
// success: (res) => {
// this.setData({
// teacher: res.result.data
// })
// // console.log(this.data.teacher[1]);
// }
// })
},
changeTab(e) {
const index = e.currentTarget.dataset.index;
this.setData({
currentTab: parseInt(index)
});
},
onAddButtonTap: function() {
this.setData({
showPopup: true
});
},
onBackButtonTap: function() {
this.setData({
showPopup: false
});
},
handleSubmit: function() {
// 处理....
// 提交成功后隐藏弹窗
this.setData({
showPopup: false
});
},
navToComments: () => {
wx.navigateTo({
url: '/pages/Comments/Comments',
})
},
inputChange(e) {
this.setData({
inputValue: e.detail.value
});
},
reserve(e){
wx.showModal({
title: '预约',
content: '确定预约该老师吗?',
complete: (res) => {
if (res.cancel) {
}
if (res.confirm) {
var tmp = this.data.teacher
var state = 0
console.log("[MAIN] reserve",tmp);
try {
state = wx.getStorageSync(tmp._id)
} catch(e) {
console.log(e);
}
if (state == 1) {
wx.showToast({
title: '您已经预约!',
icon: "error",
})
} else {
wx.setStorageSync(tmp._id, 1)
wx.showToast({
title: '请等待老师确认',
})
}
// wx.clearStorageSync(tmp._id)
}
}
})
}
})

@ -0,0 +1,20 @@
<!--pages/detail/detail.wxml-->
<!--pages/Detailed/detailed.wxml-->
<view style="background-color: #FFFFFF; margin: 15rpx; border-radius: 25rpx;">
<view class="avatar-wrapper">
<image class="avatar" src="{{avatarUrl}}"></image>
<text style="width: 50%; line-height: 1.5;">
姓名: {{teacher.name}}
科目: {{teacher.subject}}
空闲时间:\n {{teacher.freetime}}
学历: {{teacher.education}}
时薪: {{teacher.salary}}
</text>
</view>
<view style="margin-left: 20rpx;">联系方式:\t{{teacher.phone}}</view>
<view style="margin-left: 20rpx;">EMAIL\t{{teacher.email}}</view>
<view style="margin: 20rpx;">综合评分: 10.0</view>
</view>
<button type="primary" bind:tap="reserve">预约</button>

@ -1,4 +1,4 @@
/* pages/Detailed/detailed.wxss */
/* pages/detail/detail.wxss *//* pages/Detailed/detailed.wxss */
.avatar-wrapper {
display: flex;
justify-content: left;

@ -1,70 +0,0 @@
// pages/Detailed/detailed.js
const defaultAvatarUrl = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
Page({
/**
* 页面的初始数据
*/
data:{
avatarUrl: defaultAvatarUrl,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
var index = options.index; // 第一个页面传递过来的教师索引
var teacherInfo = this.data.teacher[index];
// 使用索引在教师数组中获取对应的教师信息
// var teacherInfo = this.data.teacher[index];
// 继续处理教师信息
// 调用云函数获取教师信息
wx.cloud.callFunction({
name: "detailed",
success: (res) => {
this.setData({
teacher: res.result.data
})
// console.log(this.data.teacher[1]);
}
})
},
changeTab(e) {
const index = e.currentTarget.dataset.index;
this.setData({
currentTab: parseInt(index)
});
},
onAddButtonTap: function() {
this.setData({
showPopup: true
});
},
onBackButtonTap: function() {
this.setData({
showPopup: false
});
},
handleSubmit: function() {
// 处理....
// 提交成功后隐藏弹窗
this.setData({
showPopup: false
});
},
navToComments: () => {
wx.navigateTo({
url: '/pages/Comments/Comments',
})
},
inputChange(e) {
this.setData({
inputValue: e.detail.value
});
},
})

@ -1,15 +0,0 @@
<!--pages/Detailed/detailed.wxml-->
<view style="background-color: #FFFFFF; margin: 15rpx; border-radius: 25rpx;">
<view class="avatar-wrapper">
<image class="avatar" src="{{avatarUrl}}"></image>
<text>
姓名: 老师
科目: NULL
空闲时间: NULL
学历: NULL
时薪: NULL
</text>
</view>
<view style="margin: 20rpx;">综合评分: 10.0</view>
</view>

@ -1,4 +1,4 @@
const app = getApp();
//const that = this;
const db = wx.cloud.database().collection("student_Demands");
// pages/MainTest/MainTest.js
Page({
@ -9,19 +9,19 @@ Page({
data: {
currentTab: 0,
showPopup: false,
teacher: {},
teacher: [],
recTeacher: [],
exist: false,
rec: false,
selectedIndex: -1,
subject: ['英语','语文','C','Java','Python','高等数学','物理','历史','政治'],
index: 0,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// wx.showLoading({
// title: '加载中',
// })
//检测用户是否注册过
// wx.cloud.database()
// .collection('user_Data')
@ -42,7 +42,18 @@ Page({
this.setData({
teacher: res.result.data
})
// console.log(this.data.teacher[1]);
console.log("[MAIN]",this.data.teacher);
}
})
},
onShow() {
wx.cloud.callFunction({
name: "recommend",
success: (res) => {
this.setData({
teacher: res.result.data
})
console.log("[MAIN]",this.data.teacher);
}
})
},
@ -50,8 +61,13 @@ Page({
changeTab(e) {
const index = e.currentTarget.dataset.index;
this.setData({
rec: false,
recTeacher: null,
currentTab: parseInt(index)
});
if (index == 1) {
this.initrec()
}
},
onAddButtonTap: function() {
this.setData({
@ -72,39 +88,123 @@ Page({
});
},
navToComments: () => {
navToComments: (e) => {
wx.navigateTo({
url: '/pages/Comments/Comments',
url: '/pages/Comments/comments?id=' + e.currentTarget.dataset.id,
})
},
navToDetails: () => {
navToDetails(e) {
// console.log(e);
// console.log(e.currentTarget.dataset.item);
const tmp = e.currentTarget.dataset.item
wx.navigateTo({
url: '/pages/Details/details',
url: '/pages/Details/detail',
success: function(res) {
// 通过eventChannel向被打开页面传送数据
res.eventChannel.emit('acceptDataFromOpenerPage', { data: 'test' })
res.eventChannel.emit('acceptDataFromOpenerPage', { data: tmp })
}
})
},
// contactTeacher(event) {
// // console.log(event);
// wx.makePhoneCall({
// phoneNumber: event.currentTarget.dataset.item,
// })
// }
contactTeacher(event) {
console.log(event);
wx.makePhoneCall({
phoneNumber: event.currentTarget.dataset.item,
})
},
inputChange(e) {
this.setData({
inputValue: e.detail.value
});
},
bindPickerChange(e) {
var tmplist = []
for (let i = 0; i < this.data.teacher.length; i++) {
const element = this.data.teacher[i];
if (element.subject == this.data.subject[this.data.index]) {
tmplist.push(element)
}
}
this.setData({
index: e.detail.value,
recTeacher: tmplist
})
},
cheackBoxGroupChange(e) {
// console.log(e);
var tmp = e.detail.value
var tmplist = []
for (let i = 0; i < this.data.teacher.length; i++) {
const element = this.data.teacher[i];
var freetime = element.freetime
// console.log(this.data.index);
if (freetime != undefined) {
if (element.subject == this.data.subject[this.data.index]) {
if (tmp.every((el) => freetime.includes(el))) {
tmplist.push(element)
}
}
}
}
this.setData({
recTeacher: tmplist
})
console.log("[MAIN] rec",tmplist);
console.log(this.data);
},
recommend() {
// TODO
this.setData({
rec: true
rec: true,
// recTeacher: tmplist
})
console.log("[MAIN] rec",this.data.recTeacher);
},
initrec() {
const tmplist = []
for (let i = 0; i < this.data.teacher.length; i++) {
const element = this.data.teacher[i];
if (element.subject == this.data.subject[this.data.index]) {
tmplist.push(element)
}
}
this.setData({
recTeacher: tmplist,
rec: false
})
},
reserved(e){
wx.showModal({
title: '预约',
content: '确定预约该老师吗?',
complete: (res) => {
if (res.cancel) {
}
if (res.confirm) {
var tmp = e.currentTarget.dataset.item
var state = 0
console.log("[MAIN] reserve",tmp);
try {
state = wx.getStorageSync(tmp._id)
} catch(e) {
console.log(e);
}
if (state == 1) {
wx.showToast({
title: '您已经预约!',
icon: "error",
})
} else {
wx.setStorageSync(tmp._id, 1)
wx.showToast({
title: '请等待老师确认',
})
}
// wx.clearStorageSync(tmp._id)
}
}
})
console.log("rec");
}
})

@ -2,21 +2,28 @@
<view class="cover">
<swiper indicator-dots="{{true}}" autoplay="{{true}}" interval="{{3000}}" duration="{{500}}">
<swiper-item>
<image src="cloud://cloud1-7gyjwcyfbdf819da.636c-cloud1-7gyjwcyfbdf819da-1321167991/slide_show1.png" class="slide" />
<image src="cloud://cloud1-7gyjwcyfbdf819da.636c-cloud1-7gyjwcyfbdf819da-1321167991/1.png" class="slide" />
</swiper-item>
<swiper-item>
<image src="cloud://cloud1-7gyjwcyfbdf819da.636c-cloud1-7gyjwcyfbdf819da-1321167991/slide_show2.png" class="slide" />
<image src="cloud://cloud1-7gyjwcyfbdf819da.636c-cloud1-7gyjwcyfbdf819da-1321167991/2.png" class="slide" />
</swiper-item>
<swiper-item>
<image src="cloud://cloud1-7gyjwcyfbdf819da.636c-cloud1-7gyjwcyfbdf819da-1321167991/slide_show3.png" class="slide" />
<image src="cloud://cloud1-7gyjwcyfbdf819da.636c-cloud1-7gyjwcyfbdf819da-1321167991/3.png" class="slide" />
</swiper-item>
</swiper>
</view>
<view class="divLine"></view>
<view class="tab">
<view class="tab-item" bindtap="changeTab" data-index="0">教员列表</view>
<view class="tab-item" bindtap="changeTab" data-index="1">系统推荐</view>
<view class="tab-item" bindtap="changeTab" data-index="0">
<text>教员列表</text>
<view class="divLine" style="height: 3px; background: #1AAD19;" wx:if="{{!currentTab}}"></view>
</view>
<view class="tab-item" bindtap="changeTab" data-index="1">
<text>系统推荐</text>
<view class="divLine" style="height: 3px; background: #1AAD19;" wx:if="{{currentTab}}"></view>
</view>
</view>
<view class="content">
@ -32,25 +39,25 @@
</view>
</view>
<view class="show_information" wx:for="{{teacher}}" data-index="{{index}}"wx:key="_id">
<view class="show_information" wx:for="{{teacher}}" wx:key="_id">
<!-- //当每通过一个教员上传的简历信息则创建一个 -->
<view class="content-container" bind:tap="navToDetails">
<image class="image" src="/images/user.png"></image>
<view class="content-container" bind:tap="navToDetails" data-item="{{item}}">
<image class="image" src="{{item.headPhoto?item.headPhoto:'/images/user.png'}}"/>
<view style="background: #E0E3DA; height: 150rpx; width: 1px; margin-left: 20rpx;"></view>
<view class="text-container">
<text class="title">{{item.name}}</text>
<view>
<text class="description">
电话号码:{{item.phone}}
email{{item.email}}
其他信息null
</text>
</view>
<text class="description">
科目:{{item.subject}}
学历:{{item.education}}
时薪:{{item.salary}}
</text>
</view>
</view>
<view class="divLine"></view>
<view class="button-container">
<button bind:tap="navToComments" class="button">评论</button>
<!-- <button bind:tap="contactTeacher" data-item="{{item.phone}}" class="button">联系</button> -->
<button bind:tap="reserved" class="button">预约</button>
<button bindtap="navToComments" data-id="{{item._id}}" class="button">评论</button>
<view style=" height: 100%; width: 5rpx; margin-left: 10rpx;"></view>
<button bindtap="reserved" data-item="{{item}}" class="button">预约</button>
</view>
</view>
<view class="desc">这里是家教帮平台,再也不用担心孩子的学习了</view>
@ -60,93 +67,94 @@
<!-- 学员发布的要求界面 -->
<view wx:if="{{currentTab === 1}}">
<picker bindchange="bindPickerChange" value="{{index}}" range="{{['C','Java','Python','高等数学']}}">
<view class="picker">
科目:{{array[index]}}当前选择科目
</view>
</picker>
<checkbox-group bindchange="test" style="display: grid; grid-template-columns: repeat(8, 1fr); border-style: solid; margin: 10rpx; border-radius: 15px; padding: 10rpx;">
<view>\</view>
<view>周一</view>
<view>周一</view>
<view>周一</view>
<view>周一</view>
<view>周一</view>
<view>周一</view>
<view>周一</view>
<view>上午</view>
<checkbox value="mon-m"/>
<checkbox value="mon-a"/>
<checkbox value="mon-a"/>
<checkbox value="mon-a"/>
<checkbox value="mon-a"/>
<checkbox value="mon-a"/>
<checkbox value="mon-a"/>
<view>上午</view>
<checkbox value="mon-m"/>
<checkbox value="mon-a"/>
<checkbox value="mon-a"/>
<checkbox value="mon-a"/>
<checkbox value="mon-a"/>
<checkbox value="mon-a"/>
<checkbox value="mon-a"/>
</checkbox-group>
<picker bindchange="bindPickerChange" value="{{index}}" range="{{subject}}">
<view class="picker">
科目:{{subject[index]}}
</view>
</picker>
<checkbox-group bindchange="cheackBoxGroupChange" class="checkBoxGroup">
<view> </view>
<view>周一</view>
<view>周二</view>
<view>周三</view>
<view>周四</view>
<view>周五</view>
<view>周六</view>
<view>周日</view>
<view>上午</view>
<checkbox value="周一上"/>
<checkbox value="周二上"/>
<checkbox value="周三上"/>
<checkbox value="周四上"/>
<checkbox value="周五上"/>
<checkbox value="周六上"/>
<checkbox value="周日上"/>
<view>下午</view>
<checkbox value="周一下"/>
<checkbox value="周二下"/>
<checkbox value="周三下"/>
<checkbox value="周四下"/>
<checkbox value="周五下"/>
<checkbox value="周六下"/>
<checkbox value="周日下"/>
</checkbox-group>
<view>
<button style="margin-bottom: 20rpx;" bind:tap="recommend">确定</button>
</view>
<!-- 分割线 -->
<view class="divLine"></view>
<view>
<button style="margin-bottom: 20rpx;" bind:tap="recommend">确定</button>
</view>
<!-- 分割线 -->
<view class="divLine"></view>
<view class="show_information" wx:if="{{rec}}" wx:for="{{teacher}}" wx:key="_id">
<view class="content-container">
<image class="image" src="/images/user.png"></image>
<view class="text-container">
<text class="title">{{item.name}}</text>
<view>
<text class="description">
电话号码:{{item.phone}}
email{{item.email}}
其他信息null
</text>
</view>
</view>
</view>
<view class="button-container">
<button bind:tap="navToComments" class="button">评论</button>
<!-- <button bind:tap="contactTeacher" data-item="{{item.phone}}" class="button">联系</button> -->
<button bind:tap="reserved" class="button">预约</button>
<view class="show_information" wx:if="{{rec}}" wx:for="{{recTeacher}}" wx:key="_id">
<view class="content-container" bind:tap="navToDetails" data-item="{{item}}">
<image class="image" src="{{item.headPhoto?item.headPhoto:'/images/user.png'}}"></image>
<view class="text-container">
<text class="title">{{item.name}}</text>
<view>
<text class="description">
科目:{{item.subject}}
学历:{{item.education}}
时薪:{{item.salary}}
</text>
</view>
</view>
</view>
<view class="button-container">
<button bindtap="navToComments" data-id="{{item._id}}" class="button">评论</button>
<!-- <button bind:tap="contactTeacher" data-item="{{item.phone}}" class="button">联系</button> -->
<button bind:tap="reserved" data-item="{{item}}" class="button">预约</button>
</view>
</view>
<view class="page-container">
<view class="page-container">
<!-- 页面内容 -->
<view class="popup" hidden="{{!showPopup}}">
<view class="popup" hidden="{{!showPopup}}">
<!-- 弹窗内容 -->
<view class="container">
<view class="form-box">
<view class="form-group">
<text>需求标题:</text>
<input type="text" placeholder="请输入需求标题" bindinput="handleInputTitle" />
</view>
<view class="form-group">
<text>需求内容:</text>
<textarea placeholder="请输入需求内容" bindinput="handleInputContent"></textarea>
</view>
<view class="form-group">
<text>联系方式:</text>
<input type="text" placeholder="请输入联系方式" bindinput="handleInputContact" />
</view>
<view class="container">
<view class="form-box">
<view class="form-group">
<text>需求标题:</text>
<input type="text" placeholder="请输入需求标题" bindinput="handleInputTitle" />
</view>
<view class="form-group">
<text>需求内容:</text>
<textarea placeholder="请输入需求内容" bindinput="handleInputContent"></textarea>
</view>
<view class="form-group">
<text>联系方式:</text>
<input type="text" placeholder="请输入联系方式" bindinput="handleInputContact" />
</view>
<button type="primary" bindtap="onTapButton">提交</button>
<button class="btn-back" bindtap="onBackButtonTap">返回</button>
</view>
<!-- 弹窗内容 -->
</view>
<!-- <image src="/images/add.png" class="add-button" bind:tap="onAddButtonTap" /> -->
<button type="primary" bindtap="onTapButton">提交</button>
<button class="btn-back" bindtap="onBackButtonTap">返回</button>
</view>
<!-- 弹窗内容 -->
</view>
<!-- <image src="/images/add.png" class="add-button" bind:tap="onAddButtonTap" /> -->
</view>
</view>
</view>

@ -3,42 +3,49 @@
.slide{
display: flex;
width: 100%;
height: 90%;
height: 100%;
background-color: #ffffff;
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);
border-radius: 20px;
}
.tab {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #e8edf7;
background-color: #f1f1f1;
}
.tab-item {
flex: 1;
text-align: center;
padding: 10rpx;
font-size: 50rpx;
font-size: 35rpx;
color: #1f1919;
cursor: pointer;
}
.tab-item.active {
color: #2eaa23;
font-weight: bold;
border-bottom: 2rpx solid #333333;
.tab-item > text {
display: block;
height: 60rpx;
}
.show_information {
width: 90%;
margin: 20rpx auto;
padding: 20rpx;
background-color: #bce3fa;
background-color: #ffffff;
border-radius: 25rpx;
box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);
border-color: #b8bec7;
border-style: solid;
border-width: 1px;
}
.search-wrapper {
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.1); /* 设置阴影 */
padding: 10px; /* 设置内边距 */
/* padding: 10px; 设置内边距 */
padding-left: 10px;
padding-right: 10px;
}
.search-bar {
@ -81,16 +88,15 @@
.text-container {
flex: 1;
padding: 20rpx;
/* padding: 20rpx; */
}
.text-container > button {
display: inline;
position: relative;
left: 300rpx;
top: 35rpx;
.button {
border-radius: 10px;
background-color: #4CAF50;
color: #fff;
font-size: 32rpx;
padding: 14rpx;
}
.title {
@ -104,6 +110,9 @@
font-size: 28rpx;
color: #666;
line-height: 1.5;
display: block;
margin-top: -20rpx;
margin-left: 30rpx;
}
.button-container {
@ -112,17 +121,6 @@
margin-top: 20rpx;
}
.button {
/* flex: 1; */
background-color: #48477c;
color: #fff;
font-size: 28rpx;
padding: 10rpx;
border-radius: 8rpx;
text-align: center;
/* margin-right: 10rpx; */
}
.button-wrapper {
display: flex;
justify-content: space-between;
@ -155,7 +153,8 @@ button {
.cover{
background-color: #f1f1f1;
text-align: center;
padding: 10rpx;
padding: 15rpx;
margin-bottom: 30rpx;
}
.app{
width: 160rpx;
@ -555,10 +554,16 @@ page {
}
.picker {
height: 80rpx;
line-height: 80rpx;
margin: 10rpx;
border-radius: 15px;
border-style: solid;
border-radius: 10px;
padding: 10rpx;
background-color: #ffffff;
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);
border-color: #b8bec7;
border-style: solid;
border-width: 1px;
}
/*分割线样式*/
@ -567,3 +572,22 @@ page {
width: 100%;
height: 5rpx;
}
.checkBoxGroup {
display: grid;
grid-template-columns: repeat(8, 1fr);
border-style: solid;
margin: 10rpx;
border-radius: 10px;
padding: 10rpx;
border-width: 1px;
/* border-color: ; */
background-color: #ffffff;
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);
border-color: #b8bec7;
border-style: solid;
border-width: 1px;
}
.checkBoxGroup > checkbox {
margin: 5rpx;
}

@ -5,14 +5,16 @@ Page({
* 页面的初始数据
*/
data: {
currentTab: 0
currentTab: 0,
orders: [],
isTeacher: true,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.refreshPage()
},
/**
@ -26,7 +28,7 @@ Page({
* 生命周期函数--监听页面显示
*/
onShow() {
this.refreshPage()
},
/**
@ -47,7 +49,7 @@ Page({
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
this.refreshPage()
},
/**
@ -69,4 +71,70 @@ Page({
currentTab: parseInt(index)
});
},
confirm(e) {
const index = e.currentTarget.dataset.index
const str = this.data.stoOrder[index+2]
try {
wx.setStorageSync(str, 2)
} catch(e) {
console.error(e);
}
this.refreshPage()
},
reject(e) {
const index = e.currentTarget.dataset.index
const str = this.data.stoOrder[index+2]
try {
wx.setStorageSync(str, 0)
} catch(e) {
console.error(e);
}
this.refreshPage()
},
compelete(e) {
const index = e.currentTarget.dataset.index
const str = this.data.stoOrder[index+2]
try {
wx.setStorageSync(str, 3)
} catch(e) {
console.error(e);
}
this.refreshPage()
},
cancel(e) {
const index = e.currentTarget.dataset.index
const str = this.data.stoOrder[index+2]
try {
wx.removeStorageSync(str)
} catch(e) {
console.error(e);
}
this.refreshPage()
},
refreshPage() {
//获取订单信息
try {
const res = wx.getStorageInfoSync()
// console.log(res);
const arr = [];
console.log("[ORDERS] orders",res);
for (let i = 2; i < res.keys.length; i++) {
const str = res.keys[i];
const state = wx.getStorageSync(str)
arr.push({
id: str.substring(1,10),
state: state,
})
}
this.setData({
orders: arr,
stoOrder: res.keys
})
} catch (e) {
// Do something when catch error
console.error(e);
}
// console.log("[ORDERS] getOrders",this.data.orders);
// console.log("[ORDERS] data",this.data);
}
})

@ -2,59 +2,79 @@
<!--pages/order/index.wxml-->
<view class="tab">
<view class="tab-item" bindtap="changeTab" data-index="0">已预约</view>
<view class="tab-item" bindtap="changeTab" data-index="1">进行中</view>
<view class="tab-item" bindtap="changeTab" data-index="2">已完成</view>
<view class="tab-item" bindtap="changeTab" data-index="0">
<text>未完成</text>
<view class="div-line" wx:if="{{!currentTab}}"></view>
</view>
<view class="tab-item" bindtap="changeTab" data-index="1">
<text>已完成</text>
<view class="div-line" wx:if="{{currentTab}}"></view>
</view>
</view>
<view class="reserved" wx:if="{{currentTab==0}}">
<view class="content-container">
<image class="image" src="/images/user.png"></image>
<view class="text-container">
<text class="title">已预约订单名称</text>
<view>
<view class="content-container" wx:for="{{orders}}" wx:if="{{item.state == 1 || item.state == 2}}" wx:key="id">
<view class="text-container">
<text class="title" wx:if="{{item.state==1}}">订单状态: 等待确认</text>
<text class="title" wx:if="{{item.state==2}}">订单状态:等待完成</text>
<text class="description">
订单ID{{item.id}}
电话号码:{{item.phone}}
email{{item.email}}
其他信息null
</text>
</view>
</view>
<view wx:if="{{isTeacher && item.state==1}}">
<button size="mini" style="width: 150rpx; position: relative; left: 230rpx; top: 1rpx;" type="primary" bind:tap="confirm" data-index="{{index}}">
确认
</button>
<view style="height: 5px;"></view>
<button size="mini" style="width: 150rpx; position: relative; left: 230rpx; top: 0rpx; background-color: red;" type="primary" bind:tap="reject" data-index="{{index}}">
拒绝
</button>
</view>
<view wx:if="{{isTeacher && item.state == 2}}">
<button size="mini" style="width: 150rpx; position: relative; left: 230rpx; top: 1rpx;" type="primary" bind:tap="compelete" data-index="{{index}}">
完成
</button>
</view>
<view wx:if="{{!isTeacher}}">
<button size="mini" style="width: 150rpx; position: relative; left: 230rpx; top: 1rpx;" type="primary" bind:tap="cancel" data-index="{{index}}">
取消
</button>
</view>
</view>
</view>
<view wx:if="{{currentTab==1}}">
<view wx:if="{{currentTab==99}}">
<view class="reserved">
<view class="content-container">
<image class="image" src="/images/user.png"></image>
<view class="text-container">
<text class="title">进行中订单名称</text>
<view>
<text class="description">
电话号码:{{item.phone}}
email{{item.email}}
其他信息null
</text>
</view>
</view>
<view class="text-container">
<text class="title">进行中订单名称</text>
<text class="description">
电话号码:{{item.phone}}
email{{item.email}}
其他信息null
</text>
</view>
</view>
</view>
</view>
<view wx:if="{{currentTab==2}}">
<view class="reserved">
<view class="content-container">
<image class="image" src="/images/user.png"></image>
<view class="text-container">
<text class="title">已完成订单名称</text>
<view>
<text class="description">
电话号码:{{item.phone}}
email{{item.email}}
其他信息null
</text>
</view>
</view>
</view>
<view class="reserved" wx:if="{{currentTab==1}}">
<view class="content-container" wx:for="{{orders}}" wx:if="{{item.state == 3}}" wx:key="id">
<view class="text-container">
<text class="title" >订单状态: 完成</text>
<text class="description">
订单ID{{item.id}}
电话号码:{{item.phone}}
email{{item.email}}
其他信息null
</text>
</view>
</view>
</view>

@ -3,19 +3,27 @@
.tab{
display: flex;
justify-content: space-around;
background-color: #c9d9f5;
background-color: #ffffff;
height: 45px;
border-radius: 0 0 30rpx 30rpx;
}
.tab-item{
font-size: 20px;
font-size: 18px;
align-self: center;
margin: 10rpx;
}
.div-line {
background: #1AAD19;
height: 5rpx;
width: 55px;
margin-top: 5rpx;
}
.content-container {
display: flex;
align-items: center;
border: rgb(151, 68, 68);
padding-bottom: 40rpx;
margin-bottom: 20rpx;
border-bottom: dotted 1px ;
}
.title {
@ -31,9 +39,9 @@
line-height: 1.5;
}
.image{
width: 50%;
height: 300rpx;
.content-container > image{
width: 150rpx;
height: 150rpx;
object-fit: cover;
border-radius: 10rpx 0 0 10rpx;
border: 2rpx solid #ccc; /* 添加框线样式 */
@ -44,7 +52,11 @@
/* height: 10%; */
margin: 10rpx auto;
padding: 25rpx;
background-color: #abdefc;
background-color: #ffffff;
border-radius: 10rpx;
box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
}
.text-container {
margin-left: 30rpx;
}

@ -1,4 +1,5 @@
const db = wx.cloud.database().collection("teacher_Data")
const app = getApp()
// pages/Tprofile/Tprofile.js
Page({
@ -7,16 +8,46 @@ Page({
* 页面的初始数据
*/
data: {
tempFilePaths: {}
picture: "",
tempFilePaths: null,
name: "",
subject: "",
salary: "",
education: "",
school: "",
class: "",
address: "",
freetime: [],
information: "",
subjects: ['英语','语文','C','Java','Python','高等数学','物理','历史','政治']
},
submit: (e) => {
console.log(e.detail.value)
submit: function(e){
// console.log(e.detail.value)
// console.log(this.data);
//输入检测,检测输入是否为空
if(!this.cheakFrom(e.detail.value)) {
return
}
db.add({
data: e.detail.value,
data: {
name: this.data.name,
subject: this.data.subject,
salary: this.data.salary,
education: this.data.education,
school: this.data.school,
class: this.data.class,
address: this.data.address,
freetime: this.data.freetime,
information: this.data.information,
headPhoto: this.data.tempFilePaths
},
success: (res) => {
console.log(res)
wx.showToast({
title: '提交成功',
})
console.log(this.data)
}
})
},
@ -24,15 +55,36 @@ Page({
chooseFile: function() {
var that = this
wx.chooseMessageFile({
count: 10,
count: 1,
type: 'image',
success: function(res) {
// console.log(res);
// tempFilePath可以作为img标签的src属性显示图片
var tmp = res.tempFiles
that.setData({
tempFilePaths: res.tempFiles
picture: tmp[0].path
});
//上传头像文件
wx.cloud.uploadFile({
cloudPath: 'teacher_picture/'+ app.globalData.OPENID+tmp[0].name, // 上传至云端的路径
filePath: that.data.picture, // 小程序临时文件路径
success: (res) => {
// 返回文件 ID
// console.log(res.fileID)
// const fileId = res.fileID
console.log("[TPROFILE] uploadfile",res.fileID)
that.setData({
tempFilePaths: res.fileID,
});
// console.log(that.data);
},
fail: console.error
})
console.log(res.tempFiles);
console.log(that.data.tempFilePaths[0].path);
// wx.setStorageSync('id_photo', fileId)
// console.log(fileId);
// console.log(that.data);
}
})
},
@ -41,7 +93,12 @@ Page({
* 生命周期函数--监听页面加载
*/
onLoad(options) {
//获取缓存
// var tempFilePaths = wx.getStorageSync('id_photo')
// console.log(tempFilePaths);
// this.setData({
// tempFilePaths: tempFilePaths
// })
},
/**
@ -93,4 +150,38 @@ Page({
},
bindPickerChange(e) {
this.setData({
subject: this.data.subjects[e.detail.value]
})
},
// 表单检查方法
cheakFrom: function(e) {
var hint;
if (e.name == "") {
hint = "姓名为空!"
} else if (this.data.subject == "") {
hint = "科目为空!"
} else if (e.salary == "") {
hint = "时薪为空!"
}else if (e.education == "") {
hint = "学历为空"
}else if (e.school == "") {
hint = "学校为空!"
} else if (e.address == "") {
hint = "地址为空!"
} else if (e.class == "") {
hint = "专业为空!"
} else{
return true
}
wx.showToast({
title: hint,
icon: 'error',
duration: 2000
})
return false
}
})

@ -1,38 +1,52 @@
<!--pages/Tprofile/Tprofile.wxml-->
<view>
<form bindsubmit="submit">
<view class="container">
<view class="input-group">
<text>姓名:</text>
<input type="text" name="name" placeholder="请输入您的姓名" class="input" />
</view>
<view class="input-group">
<text>科目:</text>
<input type="text" name="subject" placeholder="请输入科目" class="input" />
<view class="container" style="display: flex; flex-direction: row;">
<view class="id-image">
<image src="{{picture}}" mode="aspectFill" bind:tap="chooseFile"/>
<view style="text-align: center;">证件照</view>
</view>
<view class="input-group">
<text>时薪:</text>
<input type="number" name="salary" placeholder="请输入时薪" class="input" />
<view class="first-container-right">
<view class="input-group" >
<text>姓名:</text>
<input type="text" model:value="{{name}}" name="name" placeholder="请输入您的姓名" class="input" />
</view>
<view class="input-group">
<picker bindchange="bindPickerChange" value="{{index}}" range="{{subjects}}">
科目:<text style="color: #888888;">{{subject?subject:"请选择"}}</text>
</picker>
</view>
<view class="input-group">
<text>时薪:</text>
<input type="number" model:value="{{salary}}" name="salary" placeholder="请输入时薪" class="input" />
</view>
</view>
</view>
<view class="container">
<view class="input-group">
<text>最高学历:</text>
<input type="text" model:value="{{education}}" name="education" placeholder="输入最高学历" class="input" />
</view>
<view class="input-group">
<text>就读学校:</text>
<input type="text"name="school" placeholder="输入现/曾经就读学校" class="input" />
<input type="text" model:value="{{school}}" name="school" placeholder="输入现/曾经就读学校" class="input" />
</view>
<view class="input-group">
<text>年级专业:</text>
<input type="text"name="calss" placeholder="输入就读专业及年级" class="input" />
<input type="text" model:value="{{class}}" name="calss" placeholder="输入就读专业及年级" class="input" />
</view>
<view class="input-group">
<text>居住地址:</text>
<input type="text"name="address" placeholder="输入现居住地址" class="input" />
<input type="text" model:value="{{address}}" name="address" placeholder="输入就读专业及年级" class="input" />
</view>
</view>
<!-- <view class="container">
<view class="container">
<text>空闲时间:</text>
<view class="table-container">
<view class="row">
@ -119,48 +133,12 @@
</view>
</view>
</view>
</view> -->
<view class="container">
<text>空闲时间:</text>
<view class="table-container">
<view class="row">
<view class="cell"></view>
<view class="header-cell">周一</view>
<view class="header-cell">周二</view>
<view class="header-cell">周三</view>
<view class="header-cell">周四</view>
<view class="header-cell">周五</view>
<view class="header-cell">周六</view>
<view class="header-cell">周日</view>
</view>
<block wx:for="{{['上午', '中午', '下午']}}" wx:key="index">
<view class="row">
<view class="cell">{{item}}</view>
<block wx:for="{{[0, 1, 2, 3, 4, 5, 6]}}" wx:key="index">
<view class="checkbox-cell">
<checkbox bindchange="checkboxChange" value="{{index}}-{{loop.index0}}"></checkbox>
</view>
</block>
</view>
</block>
</view>
</view>
<view class="container">
<view class="input-group">
<text>教员风采:</text>
<input type="text" placeholder="输入个人简介、家教经验" class="input" />
</view>
<view class="input-group">
<text>风采展示图:</text>
<button bind:tap="chooseFile">选择图片</button>
<!-- <view wx:for="{{tempFilePaths}}">
<image src="{{item}}"/>
</view> -->
<!-- <input type="text" placeholder="在此插入图片" class="input" /> -->
<text>个人简介:</text>
<input type="text" model:value="{{information}}" placeholder="输入个人简介、家教经验" class="input" />
</view>
</view>
@ -170,5 +148,4 @@
</view>
</form>
</view>

@ -7,19 +7,28 @@
margin-bottom: 10rpx;
}
.first-container-right > view {
display: inline;
}
.input-group {
display: flex;
/* align-items: center; */
justify-content: flex-start;
margin-bottom: 10rpx;
align-items: center;
justify-content: center;
text-align: left;
margin-bottom: 20rpx;
}
/* .input-group > input {
} */
.text {
width: 80rpx;
font-size: 28rpx;
text-align: left;
}
.row {
display: flex;
}
@ -53,3 +62,17 @@
border: 1rpx solid #ccc;
background-color: #fff;
}
.id-image {
display: inline;
margin-right: 50rpx;
}
/* 子元素选择器 */
.id-image > image {
height: 100px;
width: 100px;
border-style: solid;
border-color: #000;
border-width: 1px;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save