org.vue 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. <template>
  2. <view class="container">
  3. <view class="bg-white" style="padding: 10rpx 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>
  57. <fs-form-item label="名称">
  58. <fs-field required placeholder="请输入组织名称" v-model="orgState.orgForm.name" border 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. </view>
  65. </view>
  66. </fs-popup>
  67. <fs-popup v-model="staffState.showAddStaff" direction="right">
  68. <view class="container">
  69. <view class="main">
  70. <fs-form>
  71. <fs-form-item label="姓名" required>
  72. <fs-field placeholder="请输入员工姓名" v-model="staffState.staff.name" border maxlength="10"></fs-field>
  73. </fs-form-item>
  74. <fs-form-item label="手机号" required>
  75. <fs-field placeholder="请输入员工手机号" v-model="staffState.staff.phone" border></fs-field>
  76. </fs-form-item>
  77. <fs-form-item label="职位">
  78. <fs-field placeholder="请输入员工职位" v-model="staffState.staff.position" border></fs-field>
  79. </fs-form-item>
  80. <fs-form-item label="照片">
  81. <fs-upload
  82. action=""
  83. size="200rpx"
  84. v-model="staffState.staff.icon"
  85. :count="1">
  86. </fs-upload>
  87. </fs-form-item>
  88. </fs-form>
  89. </view>
  90. <view class="org-ft">
  91. <fs-button block round @click="handleAddStaff">{{staffState.staff.id ? '修改员工' : '添加员工'}}</fs-button>
  92. </view>
  93. </view>
  94. </fs-popup>
  95. </view>
  96. </template>
  97. <script setup>
  98. import { reactive } from 'vue'
  99. import { getOrgList, addOrg, delOrg, editOrg, addStaff, delStaff, editStaff } from '@/services/company'
  100. const swipeOptions = [
  101. {
  102. name: '修改',
  103. bgColor: '#2975EF',
  104. type: 'edit'
  105. },
  106. {
  107. name: '删除',
  108. bgColor: '#ff6059',
  109. type: 'del'
  110. }
  111. ]
  112. const state = reactive({
  113. intoView: '',
  114. navList: [],
  115. orgList: [],
  116. curOrgList: [],
  117. curOrgId: '',
  118. curStaffList: [],
  119. loaded: false
  120. })
  121. const handleOrg = item => {
  122. state.navList.push(item)
  123. state.intoView = 'id' + item.id
  124. // 这里特意没用state.curOrgList = item.son || [],要利用引用类型的特点管理数据
  125. state.curOrgList = item.son
  126. if (!state.curOrgList) {
  127. state.curOrgList = []
  128. }
  129. state.curStaffList = item.staffs
  130. if (!state.curStaffList) {
  131. state.curStaffList = []
  132. }
  133. state.curOrgId = item.id
  134. }
  135. const handleNav = index => {
  136. state.curOrgList = state.navList[index].son
  137. state.curStaffList = state.navList[index].staffs
  138. if (!state.curOrgList) {
  139. state.curOrgList = []
  140. }
  141. if (!state.curStaffList) {
  142. state.curStaffList = []
  143. }
  144. state.navList.splice(index + 1)
  145. state.showUser = false
  146. }
  147. const fetchOrgList = () => {
  148. return getOrgList().then(res => {
  149. state.orgList = res.data.results || []
  150. if (state.orgList.length) {
  151. state.orgList[0].son = state.orgList[0].son || []
  152. state.curOrgList = state.orgList[0].son
  153. state.navList = state.orgList
  154. state.curOrgId = state.orgList[0].id
  155. state.curStaffList = state.orgList[0].staffs
  156. }
  157. state.loaded = true
  158. })
  159. }
  160. fetchOrgList()
  161. // 添加组织相关逻辑
  162. const orgState = reactive({
  163. showAddOrg: false,
  164. orgForm: {
  165. name: ''
  166. },
  167. })
  168. const handleAddOrg = () => {
  169. if(!orgState.orgForm.name) {
  170. return uni.showToast({
  171. title: '请输入组织名称',
  172. icon: 'none'
  173. })
  174. }
  175. if (orgState.orgForm.id) {
  176. editOrg(orgState.orgForm).then(res => {
  177. getOrgList().then(res => {
  178. state.orgList = res.data.results || []
  179. uni.showToast({
  180. title: '修改成功',
  181. icon: 'none'
  182. })
  183. state.curOrgList.splice(orgState.orgForm.index, 1, orgState.orgForm)
  184. orgState.orgForm = {
  185. name: ''
  186. }
  187. orgState.showAddOrg = false
  188. })
  189. })
  190. } else{
  191. addOrg({
  192. ...orgState.orgForm,
  193. parentId: orgState.curOrgId
  194. }).then(res => {
  195. getOrgList().then(data => {
  196. orgState.orgList = data.data.results || []
  197. uni.showToast({
  198. title: '添加成功',
  199. icon: 'none'
  200. })
  201. orgState.curOrgList.push(res.data)
  202. orgState.orgForm = {
  203. name: ''
  204. }
  205. orgState.showAddOrg = false
  206. })
  207. })
  208. }
  209. }
  210. const handleOrgOption = ({option, data}) => {
  211. if (option.type === 'del') {
  212. uni.showModal({
  213. title: '您确定移除该组织吗?'
  214. }).then(res => {
  215. if (res.confirm) {
  216. delOrg(data.id).then(res => {
  217. uni.showToast({
  218. icon: 'none',
  219. title: '删除成功'
  220. })
  221. orgState.curOrgList.splice(data.index, 1)
  222. })
  223. }
  224. })
  225. } else if (option.type === 'edit') {
  226. orgState.showAddOrg = true
  227. orgState.orgForm = data
  228. }
  229. }
  230. // 添加员工相关逻辑
  231. const staff = {
  232. name: '',
  233. phone: '',
  234. position: '',
  235. icon: []
  236. }
  237. const staffState = reactive({
  238. showAddStaff: false,
  239. staff: {
  240. ...staff
  241. },
  242. })
  243. const handleStaffOption = ({option, data}) => {
  244. if (option.type === 'del') {
  245. uni.showModal({
  246. title: '您确定移除该员工吗?'
  247. }).then(res => {
  248. if (res.confirm) {
  249. delStaff(data.id).then(res => {
  250. uni.showToast({
  251. icon: 'none',
  252. title: '删除成功'
  253. })
  254. state.curStaffList.splice(data.index)
  255. })
  256. }
  257. })
  258. } else if (option.type === 'edit') {
  259. staffState.showAddStaff = true
  260. staffState.staff = {
  261. ...data,
  262. icon: data.icon ? [data.icon] : ''
  263. }
  264. }
  265. }
  266. const handleAddStaff = () => {
  267. if (staffState.staff.id) {
  268. editStaff({
  269. ...staffState.staff,
  270. icon: staffState.staff.icon[0] || '',
  271. }).then(res => {
  272. uni.showToast({
  273. title: '修改成功',
  274. icon: 'none'
  275. })
  276. staffState.curStaffList.splice(staffState.staff.index, 1, staffState.staff)
  277. staffState.staff = {
  278. ...staff
  279. }
  280. staffState.showAddStaff = false
  281. })
  282. } else{
  283. addStaff({
  284. ...staffState.staff,
  285. icon: staffState.staff.icon[0] || '',
  286. orgId: staffState.curOrgId,
  287. }).then(res => {
  288. uni.showToast({
  289. title: '添加成功',
  290. icon: 'none'
  291. })
  292. staffState.curStaffList.push(res.data)
  293. staffState.staff = {
  294. ...staff
  295. }
  296. staffState.showAddStaff = false
  297. })
  298. }
  299. }
  300. </script>
  301. <style lang="scss" scoped>
  302. page{
  303. height: 100%;
  304. }
  305. .org-nav{
  306. height: 60rpx;
  307. line-height: 60rpx;
  308. white-space: nowrap;
  309. &-item {
  310. display: inline-block;
  311. white-space: nowrap;
  312. & + & {
  313. padding-left: 40rpx;
  314. position: relative;
  315. &::before{
  316. position: absolute;
  317. content: '>';
  318. left: 14rpx;
  319. color: #AAAAAA;
  320. }
  321. }
  322. &:last-child{
  323. color: #3D71ED;
  324. }
  325. }
  326. }
  327. .org-ft{
  328. padding: 40rpx 0;
  329. flex-shrink: 0;
  330. text-align: center;
  331. }
  332. </style>