org.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. <template>
  2. <view class="container">
  3. <view class="bg-white" style="padding: 16rpx 20rpx;">
  4. <scroll-view scroll-x :scroll-into-view="state.intoView" class="org-nav">
  5. <view
  6. class="org-nav-item"
  7. :id="'id' + item.id"
  8. v-for="(item, index) in state.navList"
  9. :key="item.id" @click="handleNav(index)">
  10. {{item.name}}
  11. </view>
  12. </scroll-view>
  13. </view>
  14. <fs-gutter></fs-gutter>
  15. <view class="main">
  16. <fs-swipe-action-group v-if="state.curOrgList.length">
  17. <fs-swipe-action
  18. v-for="(item, index) in state.curOrgList"
  19. :key="item.id"
  20. :options="swipeOptions"
  21. :optionData="{...item, index}"
  22. @clickOption="handleOrgOption">
  23. <fs-cell arrow border :title="item.name" @click="handleOrg(item)"></fs-cell>
  24. </fs-swipe-action>
  25. </fs-swipe-action-group>
  26. <fs-swipe-action-group v-if="state.curStaffList.length">
  27. <fs-swipe-action
  28. v-for="(staff, index) in state.curStaffList"
  29. :key="staff.id" :options="swipeOptions"
  30. :optionData="{...staff, index}"
  31. @clickOption="handleStaffOption">
  32. <fs-cell border>
  33. <template #title>
  34. <fs-avatar :src="staff.icon"></fs-avatar>
  35. </template>
  36. <template #value>
  37. <view class="title">{{staff.name}}</view>
  38. <view class="sub">{{staff.phone}}</view>
  39. </template>
  40. </fs-cell>
  41. </fs-swipe-action>
  42. </fs-swipe-action-group>
  43. <fs-empty v-if="!state.curOrgList.length && !state.curStaffList.length && state.loaded"></fs-empty>
  44. </view>
  45. <fs-row gutter="30rpx" class="org-ft">
  46. <fs-col span="6">
  47. <fs-button block round @click="orgState.showAddOrg = true">添加组织</fs-button>
  48. </fs-col>
  49. <fs-col span="6">
  50. <fs-button block round @click="staffState.showAddStaff = true">添加员工</fs-button>
  51. </fs-col>
  52. </fs-row>
  53. <fs-popup v-model="orgState.showAddOrg" direction="right">
  54. <view class="container">
  55. <view class="main">
  56. <fs-form ref="orgRef" :model="orgState.orgForm">
  57. <fs-form-item label="名称" required>
  58. <fs-field placeholder="请输入组织名称" v-model="orgState.orgForm.name" maxlength="10"></fs-field>
  59. </fs-form-item>
  60. </fs-form>
  61. </view>
  62. <view class="org-ft">
  63. <!-- <fs-button block round @click="handleAddOrg">{{orgState.orgForm.id ? '修改组织' : '添加组织'}}</fs-button> -->
  64. <fs-button block round @click="handleAddOrg">保存</fs-button>
  65. </view>
  66. </view>
  67. </fs-popup>
  68. <fs-popup v-model="staffState.showAddStaff" direction="right">
  69. <view class="container">
  70. <view class="main">
  71. <fs-form
  72. ref="staffRef"
  73. :model="staffState.staff"
  74. >
  75. <fs-form-item label="姓名" required>
  76. <fs-field placeholder="请输入员工姓名" v-model="staffState.staff.name" maxlength="10"></fs-field>
  77. </fs-form-item>
  78. <fs-form-item label="手机号" required>
  79. <fs-field type="number" placeholder="请输入员工手机号" v-model="staffState.staff.phone"></fs-field>
  80. </fs-form-item>
  81. <fs-form-item label="职位">
  82. <fs-field placeholder="请输入员工职位" v-model="staffState.staff.position"></fs-field>
  83. </fs-form-item>
  84. <fs-form-item label="照片">
  85. <fs-upload
  86. action=""
  87. size="180rpx"
  88. v-model="staffState.staff.icon"
  89. :count="1">
  90. </fs-upload>
  91. </fs-form-item>
  92. </fs-form>
  93. </view>
  94. <view class="org-ft">
  95. <!-- <fs-button block round @click="handleAddStaff">{{staffState.staff.id ? '修改员工' : '添加员工'}}</fs-button> -->
  96. <fs-button block round @click="handleAddStaff">保存</fs-button>
  97. </view>
  98. </view>
  99. </fs-popup>
  100. </view>
  101. </template>
  102. <script setup>
  103. import { ref, reactive } from 'vue'
  104. import { getOrgList, addOrg, delOrg, editOrg, addStaff, delStaff, editStaff } from '@/services/company'
  105. import useForm from '@/hooks/useForm'
  106. import useValidator from '@/hooks/useValidator'
  107. const validator = useValidator()
  108. const swipeOptions = [
  109. {
  110. name: '修改',
  111. bgColor: '#2975EF',
  112. type: 'edit'
  113. },
  114. {
  115. name: '删除',
  116. bgColor: '#ff6059',
  117. type: 'del'
  118. }
  119. ]
  120. const state = reactive({
  121. intoView: '',
  122. navList: [],
  123. orgList: [],
  124. curOrgList: [],
  125. curOrgId: '',
  126. curStaffList: [],
  127. loaded: false
  128. })
  129. const handleOrg = item => {
  130. state.navList.push(item)
  131. state.intoView = 'id' + item.id
  132. // 这里特意没用state.curOrgList = item.son || [],要利用引用类型的特点管理数据
  133. state.curOrgList = item.son
  134. if (!state.curOrgList) {
  135. state.curOrgList = []
  136. }
  137. state.curStaffList = item.staffs
  138. if (!state.curStaffList) {
  139. state.curStaffList = []
  140. }
  141. state.curOrgId = item.id
  142. }
  143. const handleNav = index => {
  144. state.curOrgList = state.navList[index].son
  145. state.curStaffList = state.navList[index].staffs
  146. if (!state.curOrgList) {
  147. state.curOrgList = []
  148. }
  149. if (!state.curStaffList) {
  150. state.curStaffList = []
  151. }
  152. state.navList.splice(index + 1)
  153. state.showUser = false
  154. }
  155. const fetchOrgList = () => {
  156. return getOrgList().then(res => {
  157. state.orgList = res.data.results || []
  158. if (state.orgList.length) {
  159. state.orgList[0].son = state.orgList[0].son || []
  160. state.curOrgList = state.orgList[0].son
  161. state.navList = state.orgList
  162. state.curOrgId = state.orgList[0].id
  163. state.curStaffList = state.orgList[0].staffs
  164. }
  165. state.loaded = true
  166. })
  167. }
  168. fetchOrgList()
  169. // 添加组织相关逻辑
  170. const orgRef = ref(null)
  171. const formRules = {
  172. name: {
  173. required: true,
  174. message: '请输入组织名称',
  175. transform(value) {
  176. return value.trim()
  177. }
  178. },
  179. }
  180. const orgForm = useForm(formRules, 'orgRef')
  181. const orgState = reactive({
  182. showAddOrg: false,
  183. orgForm: {
  184. name: ''
  185. },
  186. })
  187. const handleAddOrg = () => {
  188. orgForm.validate(orgState.orgForm).then(res => {
  189. if (orgState.orgForm.id) {
  190. editOrg(orgState.orgForm).then(res => {
  191. getOrgList().then(res => {
  192. state.orgList = res.data.results || []
  193. uni.showToast({
  194. title: '修改成功',
  195. icon: 'none'
  196. })
  197. state.curOrgList.splice(orgState.orgForm.index, 1, orgState.orgForm)
  198. orgState.orgForm = {
  199. name: ''
  200. }
  201. orgState.showAddOrg = false
  202. })
  203. })
  204. } else{
  205. addOrg({
  206. ...orgState.orgForm,
  207. parentId: orgState.curOrgId
  208. }).then(res => {
  209. getOrgList().then(data => {
  210. orgState.orgList = data.data.results || []
  211. uni.showToast({
  212. title: '添加成功',
  213. icon: 'none'
  214. })
  215. orgState.curOrgList.push(res.data)
  216. orgState.orgForm = {
  217. name: ''
  218. }
  219. orgState.showAddOrg = false
  220. })
  221. })
  222. }
  223. })
  224. }
  225. const handleOrgOption = ({option, data}) => {
  226. if (option.type === 'del') {
  227. uni.showModal({
  228. title: '您确定移除该组织吗?'
  229. }).then(res => {
  230. if (res.confirm) {
  231. delOrg(data.id).then(res => {
  232. uni.showToast({
  233. icon: 'none',
  234. title: '删除成功'
  235. })
  236. orgState.curOrgList.splice(data.index, 1)
  237. })
  238. }
  239. })
  240. } else if (option.type === 'edit') {
  241. orgState.showAddOrg = true
  242. orgState.orgForm = data
  243. }
  244. }
  245. // 添加员工相关逻辑
  246. const staffRef = ref(null)
  247. const staffFormRules = {
  248. name: {
  249. required: true,
  250. message: '请输入员工姓名',
  251. transform(value) {
  252. return value.trim()
  253. }
  254. },
  255. phone: {
  256. validator: validator.mobile,
  257. },
  258. }
  259. const staffForm = useForm(staffFormRules, 'staffRef')
  260. const staff = {
  261. name: '',
  262. phone: '',
  263. position: '',
  264. icon: []
  265. }
  266. const staffState = reactive({
  267. showAddStaff: false,
  268. staff: {
  269. ...staff
  270. },
  271. })
  272. const handleStaffOption = ({option, data}) => {
  273. if (option.type === 'del') {
  274. uni.showModal({
  275. title: '您确定移除该员工吗?'
  276. }).then(res => {
  277. if (res.confirm) {
  278. delStaff(data.id).then(res => {
  279. uni.showToast({
  280. icon: 'none',
  281. title: '删除成功'
  282. })
  283. state.curStaffList.splice(data.index)
  284. })
  285. }
  286. })
  287. } else if (option.type === 'edit') {
  288. staffState.showAddStaff = true
  289. staffState.staff = {
  290. ...data,
  291. icon: data.icon ? [data.icon] : ''
  292. }
  293. }
  294. }
  295. const handleAddStaff = () => {
  296. staffForm.validate(staffState.staff).then(() => {
  297. if (staffState.staff.id) {
  298. editStaff({
  299. ...staffState.staff,
  300. icon: staffState.staff.icon[0] || '',
  301. }).then(res => {
  302. uni.showToast({
  303. title: '修改成功',
  304. icon: 'none'
  305. })
  306. staffState.curStaffList.splice(staffState.staff.index, 1, staffState.staff)
  307. staffState.staff = {
  308. ...staff
  309. }
  310. staffState.showAddStaff = false
  311. })
  312. } else{
  313. addStaff({
  314. ...staffState.staff,
  315. icon: staffState.staff.icon[0] || '',
  316. orgId: staffState.curOrgId,
  317. }).then(res => {
  318. uni.showToast({
  319. title: '添加成功',
  320. icon: 'none'
  321. })
  322. staffState.curStaffList.push(res.data)
  323. staffState.staff = {
  324. ...staff
  325. }
  326. staffState.showAddStaff = false
  327. })
  328. }
  329. })
  330. }
  331. </script>
  332. <style>
  333. page{
  334. height: 100%;
  335. }
  336. </style>
  337. <style lang="scss" scoped>
  338. .org-nav{
  339. height: 60rpx;
  340. line-height: 60rpx;
  341. white-space: nowrap;
  342. &-item {
  343. display: inline-block;
  344. white-space: nowrap;
  345. & + & {
  346. padding-left: 40rpx;
  347. position: relative;
  348. &::before{
  349. position: absolute;
  350. content: '>';
  351. left: 14rpx;
  352. color: #AAAAAA;
  353. }
  354. }
  355. &:last-child{
  356. color: #3D71ED;
  357. }
  358. }
  359. }
  360. .org-ft{
  361. padding: 40rpx 0;
  362. flex-shrink: 0;
  363. text-align: center;
  364. }
  365. </style>