lifanagju_citu 1 månad sedan
förälder
incheckning
7f1254cd4e

+ 1 - 1
api/common.js

@@ -444,7 +444,7 @@ export const socialUserBind = async (data) => {
   return request({
     url: '/app-api/menduner/system/social-user/bind',
     method: 'POST',
-    data,
+    data: {...data, application: 'recruit_application'}, // 增加一个application参数,招聘端的值如上,不填默认null后台自动认为是求职端小程序
     custom: {
       showLoading: false,
       auth: true

+ 13 - 1
api/new/position.js

@@ -29,7 +29,7 @@ export const getUnpaidOrder = (params) => {
 // 创建订单
 export const createTradeOrder = (data) => {
   return request({
-    url: '/app-api/menduner/system/recruit/trade/order/wx-program/create',
+    url: '/app-api/menduner/system/recruit/trade/order/create',
     method: "POST",
     data,
     custom: {
@@ -104,3 +104,15 @@ export const refreshJobAdvertised = (ids) => {
     }
   })
 }
+
+// 获取企业发布职位类型权限
+export const getEnterprisePubJobTypePermission = () => {
+  return request({
+    url: '/app-api/menduner/system/recruit/enterprise/get/pub-job-type-perm',
+    method: 'GET',
+    custom: {
+      showLoading: true,
+      auth: true
+    }
+  })
+}

+ 6 - 12
components/PositionList/index.vue

@@ -47,9 +47,9 @@
             <span v-if="(item?.status-0) === 99 && tab === 0" class="divider-mx" @tap="toPay(item)">发布</span>
             <span v-if="tab === 2" class="divider-mx" @tap="handleAction(1, '', item, item)">激活</span>
             <span class="divider-mx" @tap="handleDetail(item)">详情</span>
-            <!-- <view v-if="tab !== 3">
+            <view v-if="tab !== 3">
               <span class="divider-mx" :style="{'color': item.edit ? '#333' : '#999'}" @tap="handleEdit(item)">{{ '编辑' }}</span>
-            </view> -->
+            </view>
           </view>
         </view>
       </view>
@@ -76,10 +76,10 @@
 </template>
 
 <script setup>
+import { ref } from 'vue'
 // import { commissionCalculation } from '@/utils/position'
 import { timesTampChange } from '@/utils/date'
 import { formatName } from '@/utils/getText'
-import { ref } from 'vue'
 import payPopup from '@/components/payPopup'
 import {
   topJobAdvertisedCancel,
@@ -100,9 +100,6 @@ const props = defineProps({
 
 const userInfo = ref(useUserStore?.userInfo || {})
 
-// const spuId = ref('')
-// const spuName = ref('')
-// const operateObj = ref({})
 const payRef = ref()
 // 支付
 const toPay = async (val) => {
@@ -113,10 +110,6 @@ const toPay = async (val) => {
     setTimeout(() => { uni.showToast({ title: '发布成功', icon: 'success' }) }, 1000)
     return
   }
-
-  // operateObj.value = val
-  // spuId.value = val.id || ''
-  // spuName.value = val.name || ''
   payRef.value && payRef.value.handleOpen({ spuId: val?.id||'', spuName: val?.name||'', price: 1, type: 1 })
 }
 
@@ -180,12 +173,13 @@ const handleConfirm = async () => {
   }
 }
 
-// 职位关闭、激活、刷新、置顶
+// 编辑职位
 const handleEdit = async (val) => {
   if (!val.id || !val.edit) {
     uni.showToast({ title: '职位发布时间超过24小时的不支持编辑', icon: 'none', duration: 2000 })
     return
-  } 
+  }
+  // uni.navigateTo({ url: `/pagesB/positionEdit/index?id=${val.id}` })
 }
 
 // 职位详情

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 2 - 0
components/RichEditor/editor-icon.css


+ 256 - 0
components/RichEditor/index.vue

@@ -0,0 +1,256 @@
+<template>
+	<view class="container">
+		<view class="page-body">
+			<view class='wrapper'>
+				<view class='toolbar' @tap="format">
+					<view v-if="icons.zitijiacu" :class="formats.bold ? 'ql-active' : ''" class="iconfont icon-zitijiacu" data-name="bold"></view>
+					<view v-if="icons.zitixieti" :class="formats.italic ? 'ql-active' : ''" class="iconfont icon-zitixieti" data-name="italic"></view>
+					<view v-if="icons.zitixiahuaxian" :class="formats.underline ? 'ql-active' : ''" class="iconfont icon-zitixiahuaxian" data-name="underline"></view>
+					<view v-if="icons.zitishanchuxian" :class="formats.strike ? 'ql-active' : ''" class="iconfont icon-zitishanchuxian" data-name="strike"></view>
+					<view v-if="icons.juzhongduiqi" :class="formats.align === 'center' ? 'ql-active' : ''" class="iconfont icon-juzhongduiqi" data-name="align" data-value="center"></view>
+					<view v-if="icons.youduiqi" :class="formats.align === 'right' ? 'ql-active' : ''" class="iconfont icon-youduiqi" data-name="align" data-value="right"></view>
+					<view v-if="icons.zuoyouduiqi" :class="formats.align === 'justify' ? 'ql-active' : ''" class="iconfont icon-zuoyouduiqi" data-name="align" data-value="justify"></view>
+					<view v-if="icons.clearedformat" class="iconfont icon-clearedformat" @tap="removeFormat"></view>
+					<view v-if="icons.text_color" :class="formats.color === '#0000ff' ? 'ql-active' : ''" class="iconfont icon-text_color" data-name="color" data-value="#0000ff"></view>
+					<view v-if="icons.fontbgcolor" :class="formats.backgroundColor === '#00ff00' ? 'ql-active' : ''" class="iconfont icon-fontbgcolor" data-name="backgroundColor" data-value="#00ff00"></view>
+					<view v-if="icons.date" class="iconfont icon-date" @tap="insertDate"></view>
+					<view v-if="icons.checklist" class="iconfont icon--checklist" data-name="list" data-value="check"></view>
+					<view v-if="icons.youxupailie" :class="formats.list === 'ordered' ? 'ql-active' : ''" class="iconfont icon-youxupailie" data-name="list" data-value="ordered"></view>
+					<view v-if="icons.wuxupailie" :class="formats.list === 'bullet' ? 'ql-active' : ''" class="iconfont icon-wuxupailie" data-name="list" data-value="bullet"></view>
+					<view v-if="icons.undo" class="iconfont icon-undo" @tap="undo"></view>
+					<view v-if="icons.redo" class="iconfont icon-redo" @tap="redo"></view>
+					<view v-if="icons.outdent" class="iconfont icon-outdent" data-name="indent" data-value="-1"></view>
+					<view v-if="icons.indent" class="iconfont icon-indent" data-name="indent" data-value="+1"></view>
+					<view v-if="icons.fengexian" class="iconfont icon-fengexian" @tap="insertDivider"></view>
+					<view v-if="icons.charutupian" class="iconfont icon-charutupian" @tap="insertImage"></view>
+					<view v-if="icons.format_header" :class="formats.header === 1 ? 'ql-active' : ''" class="iconfont icon-format-header-1" data-name="header" :data-value="1"></view>
+					<view v-if="icons.zitixiabiao" :class="formats.script === 'sub' ? 'ql-active' : ''" class="iconfont icon-zitixiabiao" data-name="script" data-value="sub"></view>
+					<view v-if="icons.zitishangbiao" :class="formats.script === 'super' ? 'ql-active' : ''" class="iconfont icon-zitishangbiao" data-name="script" data-value="super"></view>
+					<view v-if="icons.shanchu" class="iconfont icon-shanchu" @tap="clear"></view>
+					<view v-if="icons.direction" :class="formats.direction === 'rtl' ? 'ql-active' : ''" class="iconfont icon-direction-rtl" data-name="direction" data-value="rtl"></view>
+				</view>
+ 
+				<view class="editor-wrapper">
+					<editor id="editor" class="ql-container" placeholder="开始输入..."
+					show-img-size show-img-toolbar show-img-resize
+					@statuschange="onStatusChange" @ready="onEditorReady"
+					@input="onInputChange"
+					@blur="handleBlur">
+					</editor>
+				</view>
+			</view>
+		</view>
+		<view v-if="maxLength > 0" class="word-count">
+			<text>{{ currentWordCount }}</text>
+			<text> / </text>
+			<text>{{ maxLength }}</text>
+		</view>
+	</view>
+</template>
+ 
+<script>
+	/*
+	@blur 富文本失焦事件 会传出一个自定义事件 携带富文本
+	richValue 富文本回显数据
+	*/
+	export default {
+		props:{
+			maxLength: {
+				type: Number,
+				default: 0
+			},
+			richValue: {
+				type: String,
+				default: ''
+			}
+		},
+		data() {
+			return {
+				icons: {
+          zitijiacu: true,
+          zitixieti: true,
+          zitixiahuaxian: true,
+          zitishanchuxian: false,
+          juzhongduiqi: true,
+          youduiqi: true,
+          zuoyouduiqi: true,
+          clearedformat: true,
+          text_color: false,
+          fontbgcolor: false,
+          date: false,
+          checklist: false,
+          youxupailie: true,
+          wuxupailie: true,
+          undo: true,
+          redo: true,
+          outdent: true,
+          indent: true,
+          fengexian: true,
+          charutupian: false,
+          format_header: false,
+          zitixiabiao: false,
+          zitishangbiao: false,
+          shanchu: true,
+          direction: false,
+        },
+				prevHtml: null,
+				currentWordCount: 0,
+				readOnly: false,
+				formats: {}
+			}
+		},
+		methods: {
+			handleBlur() {
+				this.editorCtx.getContents({
+					success: (res) => {
+						this.$emit('blur',res.html)
+					}
+				})
+			},
+			onInputChange(e) {
+				const text = e?.detail?.text || null
+				const html = e?.detail?.html || null
+				if (!text || !html) return
+
+				if (this.maxLength && (text.length >= this.maxLength)) {
+					uni.showToast({ icon: 'none', title: `最多可输入${this.maxLength}个字符` })
+					setEditorContent(this.prevHtml)
+					return
+				}
+
+				this.currentWordCount = text.length || 0
+				this.prevHtml = html
+			},
+			setEditorContent(content) {
+				this.editorCtx.setContents({
+					html: content
+				});
+			},
+			onEditorReady() {
+				this.createSelectorQuery().select('#editor').context((res) => {
+					this.editorCtx = res.context
+					this.setEditorContent(this.richValue)
+				}).exec()
+				// #endif	
+			},
+			undo() {
+				this.editorCtx.undo()
+			},
+			redo() {
+				this.editorCtx.redo()
+			},
+			format(e) {
+				let {name,value} = e.target.dataset
+				if (!name) return
+				this.editorCtx.format(name, value)
+			},
+			onStatusChange(e) {
+				const formats = e.detail
+				this.formats = formats
+			},
+			insertDivider() {
+				this.editorCtx.insertDivider({
+					success: function() {
+						console.log('insert divider success')
+					}
+				})
+			},
+			clear() {
+				uni.showModal({
+					title: '清空编辑器',
+					content: '确定清空全部内容?',
+					success: res => {
+						if (res.confirm) {
+							this.editorCtx.clear({
+								success: function(res) {
+									console.log("clear success")
+								}
+							})
+						}
+					}
+				})
+			},
+			removeFormat() {
+				this.editorCtx.removeFormat()
+			},
+			insertDate() {
+				const date = new Date()
+				const formatDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`
+				this.editorCtx.insertText({
+					text: formatDate
+				})
+			},
+			insertImage() {
+                // 上传图片方法 这里需要调用上传图片接口,拿到图片url并插入
+				this.$utils.uploadImage(1, (res) => {
+					this.editorCtx.insertImage({
+						src: res,
+						alt: '图像',
+						success: (res) => {
+							console.log('insert image success')
+						}
+					})
+				});
+			}
+		}
+	}
+</script>
+ 
+<style>
+@import "./editor-icon.css";
+
+.container {
+  width: 100%;
+  border: solid 1px #eee;
+}
+
+#editor {
+  width: 100%;
+  height: 300px;
+  border: solid 1px #eee;
+  word-break: break-all;
+}
+
+.wrapper {
+  padding: 5px;
+}
+
+.iconfont {
+  display: inline-block;
+  padding: 8px 8px;
+  /* width: 24px;
+  height: 24px; */
+  cursor: pointer;
+  font-size: 16px;
+}
+
+.toolbar {
+  box-sizing: border-box;
+  border-bottom: 0;
+  font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
+}
+
+.ql-container {
+  box-sizing: border-box;
+  padding: 10px;
+  width: 100%;
+  min-height: 30vh;
+  height: auto;
+  background: #fff;
+  /* margin-top: 10px; */
+  font-size: 14px;
+  line-height: 1.5;
+}
+
+.ql-active {
+  color: #06c;
+}
+
+.word-count {
+	margin-top: 10px;
+  font-size: 12px;
+  color: #999;
+	text-align: right;
+	margin: 4px;
+}
+</style>

+ 31 - 20
components/payPopup/index.vue

@@ -1,6 +1,6 @@
 <template>
   <view style="z-index: 999;">
-    <uni-popup ref="popup" :is-mask-click="false" borderRadius="10px 10px 0 0" background-color="#eee">
+    <uni-popup ref="popup" :is-mask-click="false" borderRadius="10px 10px 0 0" background-color="#eee" @change="popupChange">
       <view class="popup-content">
         <view class="popup-content-close">
           <view class="icon" @tap="handleClose">
@@ -72,13 +72,13 @@ const payType = [
     icon: 'icon-weixinzhifu',
     color: '#1AAD19'
   },
-  {
-    name: '钱包支付',
-    value: 'wallet',
-    disabled: true,
-    icon: 'icon-qianbao1',
-    color: '#00B760'
-  }
+  // {
+  //   name: '钱包支付',
+  //   value: 'wallet',
+  //   disabled: true,
+  //   icon: 'icon-qianbao1',
+  //   color: '#00B760'
+  // }
 ]
 
 // 余额和其他还没有接暂时只支持微信支付
@@ -87,7 +87,7 @@ const payTypeList = ref([])
 const getPayMethodsList = async () => {
   payTypeList.value = []
   try {
-    const res = await getEnableCodeList({appId: 15})
+    const res = await getEnableCodeList({appId: 10})
     if (!res?.data?.length) {
       return
     }
@@ -105,7 +105,7 @@ const radioChange = (e) => {
   channelValue.value = e?.detail?.value || ''
 }
 
-const tabBarControl = (show = false) => { // 显示/隐藏TabBar
+const tabBarShow = (show = false) => { // 显示/隐藏TabBar
   const currentPage = getCurrentPages()
   if (!currentPage) return
   const currentTabBar = currentPage[0]?.getTabBar?.()
@@ -114,13 +114,13 @@ const tabBarControl = (show = false) => { // 显示/隐藏TabBar
 
 const popup = ref()
 const handleClose = () => {
-  tabBarControl(true)
+  tabBarShow(true)
   popup.value.close()
 }
 const query = ref(null)
 const handleOpen = (val) => {
   query.value = val
-  tabBarControl(false)
+  tabBarShow(false)
   popup.value.open('bottom')
 }
 
@@ -214,12 +214,10 @@ const checkPayStatus = async (id) => {
     const res = await getOrderPayStatus({ id })
     if (res?.data?.status === 10) {
       if (interTimer) clearInterval(interTimer)
-      emit('paySuccess')
-      // setTimeout(async () => {
-      //   const _userInfo = await useUserStore.getUserInfo()
-      //   userInfo.value = _userInfo
-      //   getMemberList() // 刷新套餐列表
-      // }, 1500)
+      uni.showToast({ title: '支付成功', icon: 'none'})
+      setTimeout(async () => {
+        emit('paySuccess')
+      }, 1500)
     }
   } catch (error) {
     console.log(error)
@@ -259,9 +257,9 @@ const weChatMiniProgramPay = async (data) => {
     signType: 'RSA',
     paySign: payConfig.paySign,
     success: (res) => {
+      // 用户支付成功
       initIntervalFun()
-      popup.value.close()
-      uni.showToast({ title: '支付成功', icon: 'none'})
+      handleClose()
     },
     fail: (err) => {
       if (err.errMsg === 'requestPayment:fail cancel') {
@@ -317,6 +315,19 @@ onHide(() => {
   if (interTimer) clearInterval(interTimer)
 })
 
+const popupChange = (e) => {
+  if (!e || e.show === undefined) return
+  if (e.show) {
+    // 支付弹窗打开
+    tabBarShow(false)
+    if (orderInfo && orderInfo.value?.id) initIntervalFun()
+  } else {
+    // 支付弹窗关闭
+    tabBarShow(true)
+    if (interTimer) clearInterval(interTimer)
+  }
+}
+
 defineExpose({
 	handleOpen
 })

+ 96 - 0
components/positionAdd/components/baseInfo.vue

@@ -0,0 +1,96 @@
+<!--  -->
+<template>
+	<view class="f-straight wrapper">
+		<uni-forms ref="form" :modelValue="formData" :rules="rules" validateTrigger="bind" label-width="90px" label-align="right">
+			<uni-forms-item label="职位类型" name="positionId" required>
+        <view class="positionTemplate">
+          <uni-data-picker class="picker" popup-title="请选择职位类型" v-model="formData.positionId" :localdata="dictObj?.positionTreeData || []" :clear-icon="false" :map="{ text: 'nameCn', value: 'id'}"></uni-data-picker>
+          <button class="btn" type="primary" size="mini" @click="null">职位模板</button>
+        </view>
+			</uni-forms-item>
+			<uni-forms-item required label="职位名称" name="name">
+        <uni-easyinput v-model="formData.name" placeholder="请填写职位名称"></uni-easyinput>
+			</uni-forms-item>
+      <uni-forms-item label="到期时间" name="expireTime" required>
+				<view class="d-flex">
+          <picker mode="date" :value="formData.expireTime" :disabled="expireTimeDisabled" fields="month" :start="startDate" @change="expireTimeChange">
+            <view class="uni-input ss-m-t-20" :style="{'opacity': expireTimeDisabled ? '0.5' : '1'}">{{ formData.expireTime }}</view>
+          </picker>
+          <uni-data-checkbox selectedColor="#00B760" class="ss-m-l-50 ss-m-t-14" multiple v-model="sofar" :localdata="[{ text: '长期有效', value: 1 }]" @change="handleChangeSofar"></uni-data-checkbox>
+        </view>
+			</uni-forms-item>
+      <uni-forms-item label="岗位职责" name="content" required>
+        <uni-easyinput type="textarea" v-model="formData.content" autoHeight  placeholder="请输入内容"></uni-easyinput>
+        <!-- <RichEditor @blur="richEditorBlur" :richValue="formData.content" /> -->
+			</uni-forms-item>
+      <uni-forms-item label="岗位要求 " name="requirement" required>
+        <uni-easyinput type="textarea" v-model="formData.requirement" autoHeight  placeholder="请输入内容"></uni-easyinput>
+			</uni-forms-item>
+		</uni-forms>
+	</view>
+</template>
+<script setup>
+import { ref } from 'vue'
+import { dictObj } from '@/utils/position.js'
+import { getNextDate } from '@/utils/date'
+// import RichEditor from '@/components/RichEditor'
+const formData = ref({
+  positionName: '',
+  positionId: '',
+  expireTime: getNextDate(15, 'YYYY-MM-DD', 'day'),
+  name: '',
+})
+const startDate = new Date().getFullYear() + '-' + (new Date().getMonth() + 1) // 不可选时间
+const sofar = ref([])
+const expireTimeDisabled = ref(false)
+// expireTimeDisabled.value = true
+    // sofar.value = [1]
+// 至今
+const handleChangeSofar = (e) => {
+  const value = e.detail.value.length ? e.detail.value[0] : ''
+  expireTimeDisabled.value = value ? true : false
+}
+const expireTimeChange = (e) => {
+  formData.value.expireTime = e?.detail?.value
+}
+
+const rules = {
+	positionId:{
+		rules: [{required: true, errorMessage: '请选择职位类型' }]
+	},
+	name:{
+		rules: [{required: true, errorMessage: '请填写职位名称' }]
+	},
+	expireTime:{
+		rules: [{required: true, errorMessage: '请选择职位到期时间' }]
+	},
+	content:{
+		rules: [{required: true, errorMessage: '请填写岗位职责' }]
+	},
+	requirement:{
+		rules: [{required: true, errorMessage: '请填写岗位要求' }]
+	},
+}
+
+// const richEditorBlur = (content) => {
+//   formData.value.content = content
+// }
+</script>
+<style lang="scss" scoped>
+.positionTemplate {
+  display: flex;
+ .picker {
+  flex: 1;
+  margin-right: 5px;
+ }
+ .btn {
+  width: 90px;
+  line-height: 34px;
+ }
+}
+
+// :deep(.uni-forms-item__content) {
+// 	max-width: 100%;
+// 	overflow: hidden;
+// }
+</style>

+ 163 - 0
components/positionAdd/components/requirement.vue

@@ -0,0 +1,163 @@
+<!--  -->
+<template>
+	<view class="f-straight wrapper">
+		<uni-forms ref="form" :modelValue="formData" :rules="rules" validateTrigger="bind" label-width="90px" label-align="right">
+			<uni-forms-item label="招聘类型" name="type" required>
+				<uni-data-picker popup-title="请选择招聘类型" v-model="formData.type" :localdata="jobType" :clear-icon="false" :map="{ text: 'key', value: 'value'}"></uni-data-picker>
+			</uni-forms-item>
+			<uni-forms-item label="学历要求" name="eduType" required>
+				<uni-data-picker popup-title="请选择学历要求" v-model="formData.eduType" :localdata="dictObj?.edu || []" :clear-icon="false" :map="{ text: 'label', value: 'value'}"></uni-data-picker>
+			</uni-forms-item>
+			<uni-forms-item label="工作经验" name="expType" required>
+				<uni-data-picker popup-title="请选择工作经验" v-model="formData.expType" :localdata="dictObj?.exp || []" :clear-icon="false" :map="{ text: 'label', value: 'value'}"></uni-data-picker>
+			</uni-forms-item>
+      <uni-forms-item label="最低薪资" name="payFrom" required label-width="90px">
+				<view class="d-flex">
+          <uni-number-box v-model="formData.payFrom" :disabled="salary?.length" :min="1" :max="999999999" :step="1000" :width="100" @change="payChange"></uni-number-box>
+          <uni-data-checkbox v-model="salary" multiple :localdata="[{ text: '薪资面议', value: 1 }]" selectedColor="#00B760" class="ss-m-l-20" @change="salaryChange"></uni-data-checkbox>
+        </view>
+			</uni-forms-item>
+      <uni-forms-item label="最高薪资" name="payTo" required label-width="90px">
+        <uni-number-box v-model="formData.payTo" :disabled="salary?.length" :min="payToMin" :max="999999999" :step="1000" :width="100"></uni-number-box>
+			</uni-forms-item>
+			<uni-forms-item label="计薪时段" name="payUnit" required>
+				<uni-data-picker popup-title="请选择计薪时段" v-model="formData.payUnit" :orderby="salary?.length" :localdata="dictObj?.payUnit || []" :clear-icon="false" :map="{ text: 'label', value: 'value'}"></uni-data-picker>
+			</uni-forms-item>
+      <uni-forms-item label="工作城市" name="areaId" required label-width="90px">
+				<uni-data-picker popup-title="请选择工作城市" v-model="formData.areaId" :localdata="dictObj?.areaTreeData || []" :clear-icon="false" :map="{ text: 'name', value: 'id'}"></uni-data-picker>
+			</uni-forms-item>
+			<uni-forms-item required label="详情地址" name="address">
+        <uni-easyinput v-model="formData.address" placeholder="请填写详细地址"></uni-easyinput>
+			</uni-forms-item>
+		</uni-forms>
+	</view>
+</template>
+<script setup>
+import { ref } from 'vue'
+import { dictObj } from '@/utils/position.js'
+import { getEnterprisePubJobTypePermission } from '@/api/new/position'
+
+console.log('dictObj:', dictObj)
+
+const formData = ref({
+  type: '',
+  eduType: '',
+  expType: '',
+  payFrom: '',
+  payTo: '',
+  payUnit: '',
+  areaId: '',
+  address: '',
+})
+
+const rules = {
+	type:{
+		rules: [{required: true, errorMessage: '请选择招聘类型' }]
+	},
+	eduType:{
+		rules: [{required: true, errorMessage: '请选择学历要求' }]
+	},
+	expType:{
+		rules: [{required: true, errorMessage: '请选择工作经验' }]
+	},
+	// payFrom:{
+	// 	rules: salary.value?.length ? [] : [
+  //     {
+  //       required: true,
+  //       errorMessage: '请填写最低薪资',
+  //     },
+  //     {
+  //       validateFunction: function (rule, value, data, callback) {
+  //         if (value < 1) {
+  //           callback('数额不得小于1')
+  //         }
+  //         return true
+  //       }
+  //     },
+  //     {
+  //       validateFunction: function (rule, value, data, callback) {
+  //         if (!formData.value?.payTo || (Number(value) < formData.value.payTo ? Number(formData.value.payTo) : 0)) {
+  //           return true
+  //         } else {
+  //           callback('应低于最高薪资')
+  //         }
+  //       }
+  //     }
+  //   ]
+	// },
+	// payTo:{
+	// 	rules:  salary.value?.length ? [] : [
+  //     {
+  //       required: true,
+  //       errorMessage: '请填写最高薪资',
+  //     },
+  //     {
+  //       validateFunction: function (rule, value, data, callback) {
+  //         if (value < 1) {
+  //           callback('数额不得小于1')
+  //         }
+  //         return true
+  //       }
+  //     },
+  //     {
+  //       validateFunction: function (rule, value, data, callback) {
+  //         if (!formData.value?.payFrom || (Number(value) > formData.value?.payFrom ? Number(formData.value?.payFrom) : 0)) {
+  //           return true
+  //         } else {
+  //           callback('应高于最低薪资')
+  //         }
+  //       }
+  //     }
+  //   ]
+	// },
+	// payUnit: salary.value?.length ? { rules: null } : {
+	// 	rules: [{required: true, errorMessage: '请选择计薪时段' }]
+	// },
+	areaId:{
+		rules: [{required: true, errorMessage: '请选择工作城市' }]
+	},
+	address:{
+		rules: [{required: true, errorMessage: '请填写详细地址' }]
+	},
+}
+
+// 薪资面议
+const salary = ref(false)
+const salaryChange = (e) => {
+  // const value = e.detail.value.length ? e.detail.value[0] : ''
+  // if (value) {
+  // }
+}
+
+const jobType = ref([])
+const getJobTypeList = async () => {
+  const res = await getEnterprisePubJobTypePermission()
+  jobType.value = res?.data?.length ? res.data : []
+}
+getJobTypeList()
+
+const payToMin = ref(1)
+const payChange = (val) => {
+  payToMin.value = val
+  if (val > formData.value.payTo) formData.value.payTo = val
+}
+
+</script>
+<style lang="scss" scoped>
+.positionTemplate {
+  display: flex;
+ .picker {
+  flex: 1;
+  margin-right: 5px;
+ }
+ .btn {
+  width: 90px;
+  line-height: 34px;
+ }
+}
+
+// :deep(.uni-forms-item__content) {
+// 	max-width: 100%;
+// 	overflow: hidden;
+// }
+</style>

+ 39 - 0
components/positionAdd/index.vue

@@ -0,0 +1,39 @@
+<template>
+  <view class="ss-m-x-20 ss-p-b-100">
+		<uni-section class="ss-m-y-20" title="职位基本信息" >
+      <template v-slot:decoration>
+        <view class="decoration decoration1">1</view>
+      </template>
+      <BaseInfo></BaseInfo>
+    </uni-section>
+    <uni-section class="ss-m-y-20" title="岗位要求" >
+      <template v-slot:decoration>
+        <view class="decoration decoration2">2</view>
+      </template>
+      <requirement></requirement>
+    </uni-section>
+  </view>
+</template>
+<script setup>
+import BaseInfo  from './components/baseInfo.vue'
+import requirement  from './components/requirement.vue'
+
+</script>
+<style scoped lang="scss">
+.decoration {
+  color: #fff;
+  border-radius: 50%;
+  width: 15px;
+  height: 15px;
+  line-height: 15px;
+  font-size: 10px;
+  text-align: center;
+  margin-right: 4px;
+}
+.decoration1 {
+  background-color: #00B760;
+}
+.decoration2 {
+  background-color: #7a87c9;
+}
+</style>

+ 2 - 0
layout/index.vue

@@ -24,12 +24,14 @@ import authModal from './components/auth-modal.vue'
 import { useIM } from '@/hooks/useIM'
 import { watch } from 'vue'
 import { userStore } from '@/store/user'
+const emit = defineEmits(['loginSucceeded'])
 const { resetConfig } = useIM()
 const useUserStore = userStore()
 watch(() => useUserStore?.accountInfo?.userId, (newVal, oldVal) => {
   if (useUserStore.refreshToken) {
 		// 监听登录状态
     resetConfig()
+    emit('loginSucceeded')
 	}
 })
 

+ 12 - 0
pages.json

@@ -124,6 +124,18 @@
 						"navigationBarTitleText": "职位详情"
 					}
 				},
+				{
+					"path": "positionAdd/index",
+					"style": {
+						"navigationBarTitleText": "新增职位"
+					}
+				},
+				{
+					"path": "positionEdit/index",
+					"style": {
+						"navigationBarTitleText": "编辑职位"
+					}
+				},
 				{
 					"path": "agreement/index",
 					"style": {

+ 100 - 40
pages/index/position.vue

@@ -1,49 +1,60 @@
 <template>
-  <view class="box defaultBgc">
-    <uni-segmented-control :current="tab" :values="controlList" @clickItem="tabChange" styleType="text" activeColor="#00B760" style="background-color: #fff"></uni-segmented-control>
-    <scroll-view class="scrollBox" :scroll-y="true" @scrolltolower="loadingMore" style="position:relative;">
-      <view>
-        <!-- -->
-        <view class="white-bgc stick ss-p-t-20" style="border-radius: 5px;">
-          <!-- <view class="defaultBgc d-flex ss-m-x-20">
-            <view class="stickBtn" :style="`color: ${query.status==='99' ? '#00B760' :''}`" @tap=""><uni-icons v-if="query.status==='99'" class="ss-m-t-6 ss-m-r-4" color="#00B760" type="checkmarkempty" size="14"/>待发布(<text class="ss-m-x-2">{{ unpublishedCount }}</text>)</view>
-            <view class="stickBtn newPositionBtn" @tap="">发布新职位</view>
-          </view> -->
-          <!-- 搜索条 -->
-          <uni-search-bar
-            v-model="query.name"
-            radius="5"
-            placeholder="输入关键字"
-            cancelButton="none"
-            :focus="false"
-            @confirm="onSearch"
-          ></uni-search-bar>
-        </view>
-        <view v-if="!positionListData?.length && more !== 'loading'" class="d-flex flex-column align-center justify-center ss-m-t-30">
-          <view class="nodata-img-parent">
-            <image src="https://minio.citupro.com/dev/static/nodata.png" mode="widthFix" style="width: 100vw;height: 100vh;"></image>
+  <layout-page @loginSucceeded="init">
+    <view class="box defaultBgc">
+      <uni-segmented-control :current="tab" :values="controlList" @clickItem="tabChange" styleType="text" activeColor="#00B760" style="background-color: #fff"></uni-segmented-control>
+      <scroll-view class="scrollBox" :scroll-y="true" @scrolltolower="loadingMore" style="position:relative;">
+        <view>
+          <!-- -->
+          <view class="white-bgc stick ss-p-t-20" style="border-radius: 5px;">
+            <!-- <view class="defaultBgc d-flex ss-m-x-20">
+              <view class="stickBtn" :style="`color: ${query.status==='99' ? '#00B760' :''}`" @tap=""><uni-icons v-if="query.status==='99'" class="ss-m-t-6 ss-m-r-4" color="#00B760" type="checkmarkempty" size="14"/>待发布(<text class="ss-m-x-2">{{ unpublishedCount }}</text>)</view>
+              <view class="stickBtn newPositionBtn" @tap="">发布新职位</view>
+            </view> -->
+            <!-- 搜索条 -->
+            <uni-search-bar
+              v-model="query.name"
+              radius="5"
+              placeholder="输入关键字"
+              cancelButton="none"
+              :focus="false"
+              @confirm="onSearch"
+            ></uni-search-bar>
           </view>
-          <view class="color-999">暂无职位</view>
-          <view class="f-horizon-center">
-            <button type="primary" size="default" class="ss-m-t-50" style="width: 60vw;" @click="handleClick">发布新职位</button>
+          <view v-if="!positionListData?.length && more !== 'loading'" class="d-flex flex-column align-center justify-center ss-m-t-30">
+            <view class="nodata-img-parent">
+              <image src="https://minio.citupro.com/dev/static/nodata.png" mode="widthFix" style="width: 100vw;height: 100vh;"></image>
+            </view>
+            <view class="color-999">暂无职位</view>
+            <view class="f-horizon-center">
+              <button type="primary" size="default" class="ss-m-t-50" style="width: 60vw;" @click="handleClickAdd">发布新职位</button>
+            </view>
+          </view>
+          <view v-else>
+            <PositionList v-if="positionListData?.length" :tab="tab" :payable="true" :list="positionListData" :noMore="false" @refresh="refresh"></PositionList>
+            <uni-load-more :status="more" />
+            <view style="padding-bottom: 20vh;"></view>
+            <view class="addBtn" @tap="handleClickAdd">
+              <view class="addBox">
+                <view class="icon">+</view>
+                <view class="text">发布新职位</view>
+              </view>
+            </view>
           </view>
         </view>
-        <view v-else>
-          <PositionList v-if="positionListData?.length" :tab="tab" :payable="true" :list="positionListData" :noMore="false" @refresh="refresh"></PositionList>
-          <uni-load-more :status="more" />
-          <view style="padding-bottom: 20vh;"></view>
-        </view>
-      </view>
-    </scroll-view>
-  </view>
+      </scroll-view>
+    </view>
+  </layout-page>
 </template>
 
 <script setup>
 import { ref } from 'vue'
+import layoutPage from '@/layout'
 import PositionList from '@/components/PositionList'
 import { dealDictArrayData } from '@/utils/position'
 import { getJobAdvertisedList } from '@/api/new/position'
 import { onShow } from '@dcloudio/uni-app'
+import { getAccessToken } from '@/utils/request'
+import { showAuthModal } from '@/hooks/useModal'
 
 const tab = ref(2)
 const tabList = [
@@ -99,17 +110,24 @@ const getData = async () => {
     more.value = 'more'
   }
 }
-getData()
 
 // 设置自定义tabbar选中值
 onShow(() => {
+  console.log('onShow:', 789)
   const currentPage = getCurrentPages()[0] // 获取当前页面实例
   const currentTabBar = currentPage?.getTabBar?.()
 
   // 设置当前tab页的下标index
   currentTabBar?.setData({ selected: 1 })
+  init()
 })
 
+const init = () => {
+  console.log('123456:', 'init')
+  query.value.pageNo = 1
+  getData()
+}
+
 const onSearch = () => {
   query.value.pageNo = 1
   getData()
@@ -129,11 +147,16 @@ const refresh = (reset = false) => {
   getData()
 }
 
-const handleClick = () => {
-  let url = ''
-  if (url) {
-    uni.navigateTo({ url })
-  }
+const handleClickAdd = () => {
+  if (!getAccessToken()) {
+		uni.showToast({
+			title: '请先登录',
+			icon: 'none'
+		})
+		showAuthModal()
+		return
+	}
+  uni.navigateTo({ url: '/pagesB/positionAdd/index' })
 }
 
 </script>
@@ -193,6 +216,43 @@ const handleClick = () => {
   color: #00B760;
 }
 
+.addBtn{
+  position: fixed;
+  margin-bottom: 20px;
+  right: 35rpx;
+  // left: 50%;
+  // transform: translateX(-50%);
+  bottom: calc(env(safe-area-inset-bottom) + 60px);
+  width: 70px;
+  .addBox {
+    position: relative;
+    .icon {
+      font-size: 42px;
+      color: #fff;
+      background-color: #00B760;
+      width: 50px;
+      height: 50px;
+      line-height: 46px;
+      text-align: center;
+      border-radius: 50%;
+      margin: 0 auto;
+      box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
+    }
+    .text {
+      position: absolute;
+      top: 42px;
+      font-size: 12px;
+      color: #00B760;
+      background-color: #ffffffc9;
+      text-align: center;
+      padding: 2px 4px;
+      margin: 0 auto;
+      border-radius: 6px;
+      box-shadow: 0 36px 6px rgba(0, 0, 0, 0.1);
+    }
+  }
+}
+
 // :deep(.uni-searchbar) {
 //   padding: 10px 30rpx;
 // }

+ 0 - 5
pages/index/search.vue

@@ -8,11 +8,6 @@
 	import layoutPage from '@/layout'
 	import { onShow } from '@dcloudio/uni-app'
 	
-	// // 测试数据
-	// uni.switchTab({
-  //   url: '/pages/index/position'
-  // })
-	
 	// 设置自定义tabbar选中值
 	onShow(() => {
 	    const currentPage = getCurrentPages()[0];  // 获取当前页面实例

+ 9 - 0
pagesB/positionAdd/index.vue

@@ -0,0 +1,9 @@
+<template>
+  <view>
+    <positionAdd ></positionAdd>
+  </view>
+</template>
+<script setup>
+import positionAdd from '@/components/positionAdd'
+</script>
+<style scoped lang="scss"></style>

+ 5 - 0
pagesB/positionEdit/index.vue

@@ -0,0 +1,5 @@
+<template>
+  <view class="box defaultBgc">
+    positionEdit
+  </view>
+</template>

+ 22 - 1
utils/date.js

@@ -36,4 +36,25 @@ export const convertYearMonthToTimestamp = (yearMonth) => {
   const date = new Date(Date.UTC(year, month - 1, 1))
   const timestamp = date.getTime()
   return timestamp  
-}
+}
+
+export const getNextDate = (createTimestamp, format = 'YYYY-MM-DD', type, startTime) => {
+  if (type === 'day') createTimestamp = createTimestamp * 24 * 60 * 60 * 1000
+  if (type === 'hour') createTimestamp = createTimestamp * 60 * 60 * 1000
+
+  let date = new Date(startTime ? startTime + createTimestamp : new Date().getTime() + createTimestamp)
+  let year = date.getFullYear()
+  let month = date.getMonth() + 1
+  let day = date.getDate()
+
+  month = month < 10 ? `0${month}` : month
+  day = day < 10 ? `0${day}` : day
+
+  if (format === 'YYYY-MM') {
+    return `${year}-${month}`
+  } else if (format === 'YYYY-MM-DD') {
+    return `${year}-${month}-${day}`
+  } else {
+    return `${year}-${month}-${day}` // 默认返回 yyyy-mm-dd
+  }
+}

+ 3 - 0
utils/position.js

@@ -42,6 +42,9 @@ const dictList = ref([
 const getDictList = async () => {
   dictList.value.forEach(async (val) => {
     const { data } = await getDict(val.type, val.params, val.apiType)
+    // if (val.type === 'menduner_pay_unit') {
+    //   debugger
+    // }
     dictObj[val.value] = data.data
   })
 }

Vissa filer visades inte eftersom för många filer har ändrats